diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 724ace7..747aab3 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -5,10 +5,10 @@ jobs: name: Windows Test runs-on: windows-latest steps: - - name: Set up Go 1.23 + - name: Set up Go 1.25 uses: actions/setup-go@v4 with: - go-version: 1.23 + go-version: 1.25 # Caching seems to really slow down the build due to the time # taken to save the cache. diff --git a/cmd/chunks.go b/cmd/chunks.go index c0c14fa..bfaab7a 100644 --- a/cmd/chunks.go +++ b/cmd/chunks.go @@ -3,8 +3,8 @@ package main import ( "os" + kingpin "github.com/alecthomas/kingpin/v2" "github.com/davecgh/go-spew/spew" - kingpin "gopkg.in/alecthomas/kingpin.v2" "www.velocidex.com/golang/evtx" ) diff --git a/cmd/dumpevtx.go b/cmd/dumpevtx.go index 78a19c3..e0525eb 100644 --- a/cmd/dumpevtx.go +++ b/cmd/dumpevtx.go @@ -1,24 +1,24 @@ /* - Copyright 2018 Velocidex Innovations +Copyright 2018 Velocidex Innovations - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ package main import ( "os" - kingpin "gopkg.in/alecthomas/kingpin.v2" + kingpin "github.com/alecthomas/kingpin/v2" ) var ( diff --git a/cmd/extract_windows.go b/cmd/extract_windows.go index ae7c97b..8356218 100644 --- a/cmd/extract_windows.go +++ b/cmd/extract_windows.go @@ -10,9 +10,9 @@ import ( "os" "strings" + kingpin "github.com/alecthomas/kingpin/v2" _ "github.com/mattn/go-sqlite3" "golang.org/x/sys/windows/registry" - kingpin "gopkg.in/alecthomas/kingpin.v2" "www.velocidex.com/golang/binparsergen/reader" "www.velocidex.com/golang/evtx" pe "www.velocidex.com/golang/go-pe" @@ -28,6 +28,7 @@ var ( // with potential message files. The message_table paths are not // guaranteed to exists. func walkProvider(cb func(provider string, message_table string) error) error { + resolver := evtx.NewWindowsMessageResolver() channels_key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Services\EventLog`, registry.READ|registry.ENUMERATE_SUB_KEYS|registry.WOW64_64KEY) @@ -67,7 +68,8 @@ func walkProvider(cb func(provider string, message_table string) error) error { continue } - for _, message_file := range evtx.ExpandLocations(message_files) { + for _, message_file := range resolver.ExpandLocations( + message_files) { err = cb(provider_name, message_file) if err != nil { fmt.Printf("While processing %v (%v): %v\n", diff --git a/cmd/lookup.go b/cmd/lookup.go index ad22640..f4fa4f3 100644 --- a/cmd/lookup.go +++ b/cmd/lookup.go @@ -4,8 +4,8 @@ import ( "database/sql" "fmt" + kingpin "github.com/alecthomas/kingpin/v2" _ "github.com/mattn/go-sqlite3" - kingpin "gopkg.in/alecthomas/kingpin.v2" ) var ( diff --git a/cmd/parse.go b/cmd/parse.go index 1012ca4..380e257 100644 --- a/cmd/parse.go +++ b/cmd/parse.go @@ -6,7 +6,7 @@ import ( "os" "github.com/Velocidex/ordereddict" - kingpin "gopkg.in/alecthomas/kingpin.v2" + kingpin "github.com/alecthomas/kingpin/v2" "www.velocidex.com/golang/evtx" ) diff --git a/cmd/watch.go b/cmd/watch.go index cc242b1..a34f138 100644 --- a/cmd/watch.go +++ b/cmd/watch.go @@ -6,8 +6,8 @@ import ( "os" "time" + kingpin "github.com/alecthomas/kingpin/v2" "github.com/davecgh/go-spew/spew" - kingpin "gopkg.in/alecthomas/kingpin.v2" "www.velocidex.com/golang/evtx" ) diff --git a/fixtures/Event4624_windows.golden b/fixtures/Event4624_windows.golden index 865dfac..c4e5bdd 100644 --- a/fixtures/Event4624_windows.golden +++ b/fixtures/Event4624_windows.golden @@ -56,5 +56,5 @@ "TargetLinkedLogonId": 0, "ElevatedToken": "%%1843" }, - "Message": "An account was successfully logged on.\n\nSubject:\n\tSecurity ID:\t\tS-1-5-21-546003962-2713609280-610790815-1001\n\tAccount Name:\t\ttest\n\tAccount Domain:\t\tTESTCOMPUTER\n\tLogon ID:\t\t170334\n\nLogon Information:\n\tLogon Type:\t\t2\n\tRestricted Admin Mode:\t-\n\tVirtual Account:\t\tNo\r\n\n\tElevated Token:\t\tNo\r\n\n\nImpersonation Level:\t\tImpersonation\r\n\n\nNew Logon:\n\tSecurity ID:\t\tS-1-5-21-546003962-2713609280-610790815-1002\n\tAccount Name:\t\tuser\n\tAccount Domain:\t\tTESTCOMPUTER\n\tLogon ID:\t\t6003213\n\tLinked Logon ID:\t\t0\n\tNetwork Account Name:\t-\n\tNetwork Account Domain:\t-\n\tLogon GUID:\t\t00000000-0000-0000-0000-000000000000\n\nProcess Information:\n\tProcess ID:\t\t4764\n\tProcess Name:\t\tC:\\Windows\\System32\\svchost.exe\n\nNetwork Information:\n\tWorkstation Name:\tTESTCOMPUTER\n\tSource Network Address:\t::1\n\tSource Port:\t\t0\n\nDetailed Authentication Information:\n\tLogon Process:\t\tseclogo\n\tAuthentication Package:\tNegotiate\n\tTransited Services:\t-\n\tPackage Name (NTLM only):\t-\n\tKey Length:\t\t0\n\nThis event is generated when a logon session is created. It is generated on the computer that was accessed.\n\nThe subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.\n\nThe logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).\n\nThe New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.\n\nThe network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.\n\nThe impersonation level field indicates the extent to which a process in the logon session can impersonate.\n\nThe authentication information fields provide detailed information about this specific logon request.\n\t- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.\n\t- Transited services indicate which intermediate services have participated in this logon request.\n\t- Package name indicates which sub-protocol was used among the NTLM protocols.\n\t- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.\r\n" + "Message": "An account was successfully logged on.\n\nSubject:\n\tSecurity ID:\t\tS-1-5-21-546003962-2713609280-610790815-1001\n\tAccount Name:\t\ttest\n\tAccount Domain:\t\tTESTCOMPUTER\n\tLogon ID:\t\t170334\n\nLogon Information:\n\tLogon Type:\t\t2\n\tRestricted Admin Mode:\t-\n\tRemote Credential Guard:\t-\n\tVirtual Account:\t\t0\n\tElevated Token:\t\t%28\n\nImpersonation Level:\t\tImpersonation\r\n\n\nNew Logon:\n\tSecurity ID:\t\tS-1-5-21-546003962-2713609280-610790815-1002\n\tAccount Name:\t\tuser\n\tAccount Domain:\t\tTESTCOMPUTER\n\tLogon ID:\t\t6003213\n\tLinked Logon ID:\t\tNo\r\n\n\tNetwork Account Name:\t-\n\tNetwork Account Domain:\tNo\r\n\n\tLogon GUID:\t\t00000000-0000-0000-0000-000000000000\n\nProcess Information:\n\tProcess ID:\t\t4764\n\tProcess Name:\t\tC:\\Windows\\System32\\svchost.exe\n\nNetwork Information:\n\tWorkstation Name:\tTESTCOMPUTER\n\tSource Network Address:\t::1\n\tSource Port:\t\t0\n\nDetailed Authentication Information:\n\tLogon Process:\t\tseclogo\n\tAuthentication Package:\tNegotiate\n\tTransited Services:\t-\n\tPackage Name (NTLM only):\t-\n\tKey Length:\t\t0\n\nThis event is generated when a logon session is created. It is generated on the computer that was accessed.\n\nThe subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.\n\nThe logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).\n\nThe New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.\n\nThe network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.\n\nThe impersonation level field indicates the extent to which a process in the logon session can impersonate.\n\nThe authentication information fields provide detailed information about this specific logon request.\n\t- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.\n\t- Transited services indicate which intermediate services have participated in this logon request.\n\t- Package name indicates which sub-protocol was used among the NTLM protocols.\n\t- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.\r\n" } diff --git a/go.mod b/go.mod index a24d6a1..5143b95 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module www.velocidex.com/golang/evtx require ( - github.com/Velocidex/ordereddict v0.0.0-20230909174157-2aa49cc5d11d + github.com/Velocidex/ordereddict v0.0.0-20250821063524-02dc06e46238 github.com/alecthomas/assert v1.0.0 + github.com/alecthomas/kingpin/v2 v2.4.0 github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/golang-lru v1.0.2 - github.com/mattn/go-sqlite3 v1.14.24 + github.com/mattn/go-sqlite3 v1.14.32 github.com/pkg/errors v0.9.1 github.com/sebdah/goldie v1.0.0 github.com/stretchr/testify v1.9.0 - golang.org/x/sys v0.29.0 - gopkg.in/alecthomas/kingpin.v2 v2.2.6 + golang.org/x/sys v0.37.0 www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e - www.velocidex.com/golang/go-pe v0.1.1-0.20250101153735-7a925ba8334b + www.velocidex.com/golang/go-pe v0.1.1-0.20251107001057-f93001158cd9 ) require ( @@ -20,17 +20,17 @@ require ( github.com/Velocidex/pkcs7 v0.0.0-20230220112103-d4ed02e1862a // indirect github.com/Velocidex/yaml/v2 v2.2.8 // indirect github.com/alecthomas/colour v0.1.0 // indirect - github.com/alecthomas/repr v0.1.1 // indirect - github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect - github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/alecthomas/repr v0.5.2 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sergi/go-diff v1.2.0 // indirect - golang.org/x/text v0.21.0 // indirect + github.com/sergi/go-diff v1.4.0 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + golang.org/x/text v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) // replace www.velocidex.com/golang/go-pe => /home/mic/projects/go-pe/ -//replace github.com/Velocidex/ordereddict => /home/mic/projects/ordereddict +// replace github.com/Velocidex/ordereddict => /home/mic/projects/ordereddict -go 1.20 +go 1.24.0 diff --git a/go.sum b/go.sum index 01d65bb..3b036c6 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,8 @@ github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a h1:AeXPUzhU0yhID/v5JJEIkjaE85ASe+Vh4Kuv1RSLL+4= github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a/go.mod h1:ukJBuruT9b24pdgZwWDvOaCYHeS03B7oQPCUWh25bwM= -github.com/Velocidex/ordereddict v0.0.0-20220107075049-3dbe58412844/go.mod h1:Y5Tfx5SKGOzkulpqfonrdILSPIuNg+GqKE/DhVJgnpg= -github.com/Velocidex/ordereddict v0.0.0-20221110130714-6a7cb85851cd/go.mod h1:+MqO5UMBemyFSm+yRXslbpFTwPUDhFHUf7HPV92twg4= -github.com/Velocidex/ordereddict v0.0.0-20230909174157-2aa49cc5d11d h1:fn372EqKyazBxYUP5HPpBi3jId4MXuppEypEALGfvEk= github.com/Velocidex/ordereddict v0.0.0-20230909174157-2aa49cc5d11d/go.mod h1:+MqO5UMBemyFSm+yRXslbpFTwPUDhFHUf7HPV92twg4= +github.com/Velocidex/ordereddict v0.0.0-20250821063524-02dc06e46238 h1:Q2DFf1f4fiz1/V4cu2wojDKF6/DfjIY7QmfVAr/UnJY= +github.com/Velocidex/ordereddict v0.0.0-20250821063524-02dc06e46238/go.mod h1:+MqO5UMBemyFSm+yRXslbpFTwPUDhFHUf7HPV92twg4= github.com/Velocidex/pkcs7 v0.0.0-20230220112103-d4ed02e1862a h1:H7dVazNcaE80V8cy99TF7LPpwzvr1uJ4I2nDjb5ek7E= github.com/Velocidex/pkcs7 v0.0.0-20230220112103-d4ed02e1862a/go.mod h1:/fy/Eg4TQz9KkJduvZfGCnbWTQ/LKaknS2wYB52cU6c= github.com/Velocidex/yaml/v2 v2.2.8 h1:GUrSy4SBJ6RjGt43k6MeBKtw2z/27gh4A3hfFmFY3No= @@ -12,17 +11,16 @@ github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY= github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk= github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= -github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs= +github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= +github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -31,78 +29,48 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= -github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= -github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc= +github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -www.velocidex.com/golang/binparsergen v0.1.1-0.20220107080050-ae6122c5ed14/go.mod h1:Q/J/huOyH6IlY2aShigY1CnZnw5EO0+FZJgnGEBrT5Q= www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e h1:uf1AsYiIzUMJMIdFsVdrIw/BjrGzZbrsnz9xmeZmlYU= www.velocidex.com/golang/binparsergen v0.1.1-0.20240404114946-8f66c7cf586e/go.mod h1:jk+uZGukrJZWgnNH6q9tJLUnbugHEDPCQdIOmBBMXY4= -www.velocidex.com/golang/go-pe v0.1.1-0.20250101153735-7a925ba8334b h1:hOxQYDyETh4wdnCbM9Il4X+6LwonGdLnsoznqvzw48A= -www.velocidex.com/golang/go-pe v0.1.1-0.20250101153735-7a925ba8334b/go.mod h1:agYwYzeeytVtdwkRrvxZAjgIA8SCeM/Tg7Ym2/jBwmA= +www.velocidex.com/golang/go-pe v0.1.1-0.20251107001057-f93001158cd9 h1:y5nOjOAyjH36mEGZsX2Wx3XpS78e9Jk2wK1M99rn4TY= +www.velocidex.com/golang/go-pe v0.1.1-0.20251107001057-f93001158cd9/go.mod h1:rEueOnxvtbV/uFfCeD44KvC6TL9ts/1S3VNmRudSaAc= diff --git a/message_sets.go b/message_sets.go index d2ce13d..f708dba 100644 --- a/message_sets.go +++ b/message_sets.go @@ -1,8 +1,10 @@ package evtx import ( + "fmt" "regexp" "strconv" + "strings" "sync" ) @@ -16,19 +18,51 @@ type MessageSet struct { Channel string Messages map[int]string Parameters map[int]string + Filenames map[string]int } -func (self *MessageSet) AddMessage(event_id int, message string) { +func (self *MessageSet) Debug() string { self.mu.Lock() defer self.mu.Unlock() + res := "" + for k, msg := range self.Messages { + res += fmt.Sprintf(" %#x %v\n", k, strings.TrimSpace(msg)) + } + + return fmt.Sprintf("Provider: %v, Channel %v, Messages %v, Filenames %v:\n%v", + self.Provider, self.Channel, len(self.Messages), self.Filenames, res) +} + +func (self *MessageSet) AddMessage( + event_id int, message, filename string) { + + if len(message) == 0 { + return + } + + self.mu.Lock() + defer self.mu.Unlock() + + // Sometimes we get several versions of the same message for the + // same event id but different parameters. For example say EventID + // X has 2 parameters sometimes, and 3 parameters some other + // times. We need to be able to resolve the correct version of the + // message depending on the number of parameters present. + + // To do this quickly, we shift the event id 16 bits to the left + // and include the largest expansion in the bottom 16 bits. This + // allows us to store different versions of messages for the same + // event id, and also retrieve the correct message depending on + // how the event is generated. number_of_expansions := self.getLargestExpansion(message) key := event_id<<16 | number_of_expansions self.Messages[key] = message + self.Filenames[filename] = 1 } -func (self *MessageSet) AddParameter(event_id int, message string) { +func (self *MessageSet) AddParameter(event_id int, message, filename string) { self.mu.Lock() defer self.mu.Unlock() @@ -68,7 +102,11 @@ func (self *MessageSet) GetBestMessage( self.mu.Lock() defer self.mu.Unlock() - for i := number_of_expansions; i > 0; i-- { + // Ideally we have the message which interpolates the most number + // of expansions, but sometimes this is missing so we may have to + // make do with a message that interpolates less + // elements. Hopefully they are kind of related? + for i := number_of_expansions; i >= 0; i-- { key := event_id<<16 | i res, pres := self.Messages[key] if pres { diff --git a/messages_windows.go b/messages_windows.go index 88dbbf5..c08143f 100644 --- a/messages_windows.go +++ b/messages_windows.go @@ -4,9 +4,10 @@ package evtx import ( + "fmt" "os" "path/filepath" - "sort" + "regexp" "strings" lru "github.com/hashicorp/golang-lru" @@ -16,29 +17,197 @@ import ( pe "www.velocidex.com/golang/go-pe" ) +var ( + mui_dir_regex = regexp.MustCompile("^[a-z]{2}-[a-z]{2}$") + invalidGUID = errors.New("invalidGUID") + + mui_debug = 0 +) + func NewWindowsMessageResolver() *WindowsMessageResolver { cache, err := lru.New(100) if err != nil { panic(err) } - return &WindowsMessageResolver{ + res := &WindowsMessageResolver{ // string->MessageSet cache: cache, // MUI files can be found in the SxS directory - we cache that // periodically. - mui_cache: make(map[string][]string), + mui_cache: make(map[string][]string), + checked_mui_dirs: make(map[string]bool), } + + res.buildMUIcache() + + return res } type WindowsMessageResolver struct { cache *lru.Cache - mui_cache map[string][]string + mui_cache map[string][]string + checked_mui_dirs map[string]bool + + location_expander func([]string) []string } -func (self *WindowsMessageResolver) buildSxScache() { +func (self *WindowsMessageResolver) buildMUIcacheFromDir( + directory string, dir_regex *regexp.Regexp) { + + _, pres := self.checked_mui_dirs[directory] + if pres { + return + } + self.checked_mui_dirs[directory] = true + + files, err := os.ReadDir(directory) + if err != nil { + return + } + + for _, file := range files { + filename := strings.ToLower(file.Name()) + if dir_regex != nil && !dir_regex.MatchString(filename) { + continue + } + + mui_dir := filepath.Join(directory, file.Name()) + files, err := os.ReadDir(mui_dir) + if err != nil { + continue + } + + for _, file := range files { + filename := strings.ToLower(file.Name()) + if !strings.HasSuffix(filename, ".mui") { + continue + } + + fullpath := filepath.Join(mui_dir, file.Name()) + + basename := strings.TrimSuffix(filepath.Base(filename), ".mui") + locations, _ := self.mui_cache[basename] + locations = append(locations, fullpath) + self.mui_cache[basename] = locations + + if mui_debug > 0 { + fmt.Printf("buildMUIcacheFromDir: adding %v to %v\n", fullpath, basename) + } + } + } +} + +func (self *WindowsMessageResolver) buildMUIcache() { self.mui_cache = make(map[string][]string) + + system_root := os.Getenv("SystemRoot") + if system_root == "" { + system_root = "C:/Windows/" + } + + // Get MUI files from e.g. C:\Windows\System32\en-US\*.mui + self.buildMUIcacheFromDir( + filepath.Join(system_root, "System32"), mui_dir_regex) + + // Get MUI files from e.g. C:\Windows\WinSxS\*\*.mui + // This seems to slow things down a lot because there are many SxS dlls typically. + //self.buildMUIcacheFromDir( + // filepath.Join(system_root, "WinSxS"), nil) + + windir := os.Getenv("WinDir") + programfiles := os.Getenv("programfiles") + programfiles_x86 := os.Getenv("ProgramFiles(x86)") + + // Expand environment variables in paths. + replace_env_vars := func(paths []string) []string { + result := []string{} + for _, path := range paths { + path = system_root_re.ReplaceAllLiteralString(path, system_root) + + path = windir_re.ReplaceAllLiteralString(path, windir) + + if programfiles_re.FindString(path) != "" { + result = append(result, + programfiles_re.ReplaceAllLiteralString( + path, programfiles)) + result = append(result, + programfiles_re.ReplaceAllLiteralString( + path, programfiles_x86)) + } else { + result = append(result, path) + } + } + return result + } + + // When paths refer to system32 the message table may instead + // reside in the 32 bit version. + split_system32 := func(paths []string) []string { + result := []string{} + for _, path := range paths { + result = append(result, path) + + // Sometimes messages are found in the 32 bit folders. + if system32_re.FindString(path) != "" { + result = append(result, system32_re.ReplaceAllLiteralString( + path, "\\SysWow64\\")) + } + } + + return result + } + + // On international systems messages may be stored in MUI files. + include_muis := func(paths []string) []string { + result := []string{} + seen := make(map[string]bool) + + for _, path := range paths { + result = append(result, path) + + dirname := filepath.Dir(path) + + // Make sure we checked this directory for MUI files. + self.buildMUIcacheFromDir(dirname, mui_dir_regex) + + dll_name := strings.ToLower(filepath.Base(path)) + _, pres := seen[dll_name] + if pres { + continue + } + seen[dll_name] = true + + muis, pres := self.mui_cache[dll_name] + if pres { + result = append(result, muis...) + } + } + return result + } + + // Stat each file to ensure it exists + filter_files := func(paths []string) []string { + result := []string{} + for _, path := range paths { + _, err := os.Lstat(path) + if err != nil { + continue + } + result = append(result, path) + } + return result + } + + self.location_expander = func(in []string) []string { + res := filter_files( + include_muis( + split_system32( + replace_env_vars(in)))) + + return res + } } func (self *WindowsMessageResolver) getMessageSets( @@ -79,6 +248,10 @@ func (self *WindowsMessageResolver) GetMessage( return "" } + if mui_debug >= 2 { + fmt.Printf("Getting event id %#x from %v (number_of_expansions %v)\n", + event_id, message_set.Debug(), number_of_expansions) + } // Get the event if it is there return message_set.GetBestMessage(event_id, number_of_expansions) } @@ -106,85 +279,40 @@ func (self *WindowsMessageResolver) Close() {} // message table may exist in C:\Windows\System32\XXX.dll but a // localized message table also exists in // C:\Windows\System32\en-us\XXX.dll.mui -func ExpandLocations(message_file string) []string { - - // Expand environment variables in paths. - replace_env_vars := func(paths []string) []string { - system_root := os.Getenv("SystemRoot") - windir := os.Getenv("WinDir") - programfiles := os.Getenv("programfiles") - programfiles_x86 := os.Getenv("ProgramFiles(x86)") +func (self *WindowsMessageResolver) ExpandLocations( + message_file string) []string { - result := []string{} - for _, path := range paths { - path = system_root_re.ReplaceAllLiteralString( - path, system_root) - - path = windir_re.ReplaceAllLiteralString( - path, windir) - - if programfiles_re.FindString(path) != "" { - result = append(result, - programfiles_re.ReplaceAllLiteralString( - path, programfiles)) - result = append(result, - programfiles_re.ReplaceAllLiteralString( - path, programfiles_x86)) - } else { - result = append(result, path) - } - } - return result + // Message file values may be separated by ; + res := self.location_expander(strings.Split(message_file, ";")) + if mui_debug > 0 { + fmt.Printf("ExpandLocations: %v %v\n", message_file, res) } + return res +} - // When paths refer to system32 the message table may instead - // reside in the 32 bit version. - split_system32 := func(paths []string) []string { - result := []string{} - for _, path := range paths { - result = append(result, path) - - // Sometimes messages are found in the 32 bit folders. - if system32_re.FindString(path) != "" { - result = append(result, system32_re.ReplaceAllLiteralString( - path, "\\SysWow64\\")) - } - } +func (self *WindowsMessageResolver) GetMessagesByGUID( + provider_guid, channel string) (*MessageSet, error) { - return result + if mui_debug > 0 { + fmt.Printf("GetMessagesByGUID %v: %v\n", provider_guid, channel) } - include_muis := func(paths []string) []string { - result := []string{} - for _, path := range paths { - result = append(result, path) - - // Sometimes messages are found in the MUI - // files include those as well. - dll_name := filepath.Base(path) - dir_name := filepath.Dir(path) - - result = append(result, filepath.Join( - dir_name, "en-US", dll_name+".mui")) - } - return result + if len(provider_guid) == 0 { + return nil, invalidGUID } - // Message file values may be separated by ; - res := include_muis(split_system32(replace_env_vars( - strings.Split(message_file, ";")))) - sort.Slice(res, func(i, j int) bool { - return len(res[i]) > len(res[j]) - }) - return res -} + // Sometimes the provider_guid contains the {} and sometimes it does not? + provider_guid = strings.Trim(provider_guid, "{}") + key_path := fmt.Sprintf( + `Software\Microsoft\Windows\CurrentVersion\WinEVT\Publishers\{%s}`, + provider_guid) -func (self *WindowsMessageResolver) GetMessagesByGUID( - provider_guid, channel string) (*MessageSet, error) { - key_path := `Software\Microsoft\Windows\CurrentVersion\WinEVT\Publishers\{` + provider_guid + "}" provider_key, err := registry.OpenKey(registry.LOCAL_MACHINE, key_path, registry.READ|registry.ENUMERATE_SUB_KEYS|registry.WOW64_64KEY) if err != nil { + if mui_debug > 0 { + fmt.Printf("GetMessagesByGUID OpenKey %v: %v\n", key_path, err) + } return nil, err } defer provider_key.Close() @@ -204,41 +332,50 @@ func (self *WindowsMessageResolver) GetMessagesByGUID( provider = provider_guid } - return expandLocations(message_files, parameter_files, provider, channel) + return self.expandLocations( + message_files, parameter_files, provider, channel) } -func expandLocations( +func (self *WindowsMessageResolver) expandLocations( message_files, parameter_files, provider, channel string) (*MessageSet, error) { - result := &MessageSet{ + msg_set := &MessageSet{ Provider: provider, Channel: channel, Messages: make(map[int]string), Parameters: make(map[int]string), + Filenames: make(map[string]int), } - populateMessages(message_files, result.AddMessage) + self.populateMessages(message_files, msg_set.AddMessage) if parameter_files != "" { - populateMessages(parameter_files, result.AddParameter) + self.populateMessages(parameter_files, msg_set.AddParameter) } - return result, nil + return msg_set, nil } -func populateMessages(message_files string, adder func(event_id int, message string)) { - for _, message_file := range ExpandLocations(message_files) { +func (self *WindowsMessageResolver) populateMessages( + message_files string, + adder func(event_id int, message string, filename string)) { + for _, message_file := range self.ExpandLocations(message_files) { fd, err := os.Open(message_file) if err != nil { continue } defer fd.Close() - // fmt.Printf("Populating messages from %v\n", message_file) + if mui_debug > 1 { + fmt.Printf("Populating messages from %v\n", message_file) + } reader, err := reader.NewPagedReader(fd, 4096, 100) if err != nil { continue } + if mui_debug > 1 { + fmt.Printf("Loading PE file %v\n", message_file) + } pe_file, err := pe.NewPEFile(reader) if err != nil { continue @@ -250,7 +387,7 @@ func populateMessages(message_files string, adder func(event_id int, message str } for _, msg := range messages { - adder(msg.EventId, msg.Message) + adder(msg.EventId, msg.Message, message_file) } } } @@ -284,5 +421,8 @@ func (self *WindowsMessageResolver) GetMessages( return nil, err } - return expandLocations(message_files, "", provider, channel) + if mui_debug > 1 { + fmt.Printf("GetMessages %v %v: %v\n", provider, channel, message_files) + } + return self.expandLocations(message_files, "", provider, channel) }