diff --git a/cmd/extract_windows.go b/cmd/extract_windows.go index 7d8289e..ae7c97b 100644 --- a/cmd/extract_windows.go +++ b/cmd/extract_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package main @@ -160,7 +161,7 @@ func doExtract() { } defer fd.Close() - fmt.Printf("Openning message table file %v\n", message_table) + fmt.Printf("Opening message table file %v\n", message_table) reader, err := reader.NewPagedReader(fd, 4096, 100) if err != nil { diff --git a/cmd/parse.go b/cmd/parse.go index f1b7395..1012ca4 100644 --- a/cmd/parse.go +++ b/cmd/parse.go @@ -21,6 +21,9 @@ var ( parse_file_message_file = parse.Flag("messagedb", "Path to messages database."). String() + parse_file_disable_message = parse.Flag("disable_messages", "Disable message resolver."). + Bool() + start_record_id = parse.Flag("start", "First EventID to dump").Int() number_of_records = parse.Flag("number", "How many records to print"). Default("99999999").Int() @@ -78,6 +81,10 @@ func (self *parsingContext) Parse() { } func NewParsingContext() *parsingContext { + if *parse_file_disable_message { + return &parsingContext{evtx.NullResolver{}} + } + if *parse_file_message_file != "" { resolver, err := evtx.NewDBResolver(*parse_file_message_file) kingpin.FatalIfError(err, " %v", err) diff --git a/debug.go b/debug.go index f5a46bf..574839f 100644 --- a/debug.go +++ b/debug.go @@ -11,3 +11,5 @@ func debug(format string, args ...interface{}) { fmt.Printf(format, args...) } } + +func DlvBreak() {} diff --git a/evtx.go b/evtx.go index ca2c216..14c894a 100644 --- a/evtx.go +++ b/evtx.go @@ -67,6 +67,7 @@ type EVTXHeader struct { MinorVersion uint16 MajorVersion uint16 HeaderBlockSize uint16 + ChunkCount uint16 _ [76]byte FileFlags uint32 CheckSum uint32 @@ -619,6 +620,7 @@ func ReadPrefixedUnicodeString(ctx *ParseContext, is_null_terminated bool) strin buffer := ctx.ConsumeBytes(count * 2) result := UTF16LEToUTF8(buffer) debug("ReadPrefixedUnicodeString exit: %x %s\n", ctx.Offset(), string(result)) + return string(result) } @@ -648,7 +650,6 @@ func ParseOpenStartElement(ctx *ParseContext, debug("ParseOpenStartElement Enter: %x\n", ctx.Offset()) /* dependencyID := ctx.ConsumeUint16() - elementLength := ctx.ConsumeUint32() */ // ctx.SkipBytes(2 + 4) @@ -656,7 +657,9 @@ func ParseOpenStartElement(ctx *ParseContext, ctx.SkipBytes(2) } - ctx.SkipBytes(4) + elementLength := ctx.ConsumeUint32() + debug("ParseOpenStartElement elementLength: %x\n", elementLength) + nameBuffer := ReadName(ctx) attributeListLength := uint32(0) @@ -732,7 +735,8 @@ func ParseTemplateInstance(ctx *ParseContext) bool { /* tempResLen := ctx.ConsumeUint32() */ - ctx.SkipBytes(4) + template_definition_data := int(ctx.ConsumeUint32()) + debug("ParseTemplateInstance template_definition_data %x\n", template_definition_data) // Template arguments should not be unreasonable here. Just cap // them at a reasonable size. @@ -745,7 +749,7 @@ func ParseTemplateInstance(ctx *ParseContext) bool { template, pres := ctx.GetTemplateByID(short_id) if !pres { - // longID := ctx.ConsumeBytes(16) + // longGUID := ctx.ConsumeBytes(16) ctx.SkipBytes(16) templateBodyLen := int(ctx.ConsumeUint32()) @@ -847,9 +851,18 @@ func ParseTemplateInstance(ctx *ParseContext) bool { str += fmt.Sprintf("-%d", ctx.ConsumeUint32()) } arg_values[idx] = str + case 0x21: // BinXml new_ctx := ctx.Copy() - ParseBinXML(new_ctx, TemplateContext) + + // Substitution args are not encoded in template context. + /* + https://github.com/libyal/libevtx/blob/main/documentation/Windows%20XML%20Event%20Log%20(EVTX).asciidoc#token_types + > According to [MS-EVEN6] the dependency identifier is not present + > when the element start is used in a substitution token with value + > type: Binary XML (0x21). + */ + ParseBinXML(new_ctx, !TemplateContext) ctx.SkipBytes(arg.argLen) arg_values[idx] = new_ctx.CurrentTemplate().Expand(nil) @@ -943,6 +956,7 @@ func ParseBinXML(ctx *ParseContext, template_context bool) { case 0x0B /* PIDataToken */ : case 0x0C /* TemplateInstanceToken */ : keep_going = ParseTemplateInstance(ctx) + case 0x0D /* NormalSubstitutionToken */, 0x0E: /* OptionalSubstitutionToken */ keep_going = ParseOptionalSubstitution(ctx) diff --git a/fixtures/CAPI2_Operational.golden b/fixtures/CAPI2_Operational.golden new file mode 100644 index 0000000..90881eb --- /dev/null +++ b/fixtures/CAPI2_Operational.golden @@ -0,0 +1,56 @@ +{ + "System": { + "Provider": { + "Name": "Microsoft-Windows-CAPI2", + "Guid": "{5bbca4a8-b209-48dc-a8c7-b23d3e5216fb}" + }, + "EventID": { + "Value": 70 + }, + "Version": 0, + "Level": 4, + "Task": 70, + "Opcode": 0, + "Keywords": 4611686018427388032, + "TimeCreated": { + "SystemTime": 1751191789.1541903 + }, + "EventRecordID": 707, + "Correlation": { + "ActivityID": "0E2A62B5-0688-42DE-9AE1-A2AD54A8CE40" + }, + "Execution": { + "ProcessID": 23708, + "ThreadID": 17980 + }, + "Channel": "Microsoft-Windows-CAPI2/Operational", + "Computer": "ILDHBZJST3", + "Security": { + "UserID": "S-1-5-21-1332095402-2705258134-1427695613-1001" + } + }, + "UserData": { + "CryptAcquireCertificatePrivateKey": { + "Certificate": { + "fileRef": "3AA55F503B946700761B3990B569E6F248C3D31D.cer", + "subjectName": "5ae3f7e8-6eef-4e9e-8fc9-94f5d0c0862b" + }, + "Flags": { + "value": "10040", + "CRYPT_ACQUIRE_SILENT_FLAG": "true", + "CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG": "true" + }, + "EventAuxInfo": { + "ProcessName": "RuntimeBroker.exe" + }, + "CorrelationAuxInfo": { + "TaskId": "{59A34F27-7A62-4C7D-9DE1-9E1F0E2382D5}", + "SeqNumber": "2" + }, + "Result": { + "value": "0" + } + } + }, + "Message": "" + } diff --git a/parser_test.go b/parser_test.go index 602b2fe..57aa7eb 100644 --- a/parser_test.go +++ b/parser_test.go @@ -44,6 +44,22 @@ func (self *EVTXTestSuite) TestCollector() { goldie.Assert(self.T(), fixture_name, out) } +func (self *EVTXTestSuite) TestTemplates() { + cmdline := []string{ + "parse", "testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx", + "--disable_messages", + } + cmd := exec.Command(self.binary, cmdline...) + out, err := cmd.CombinedOutput() + assert.NoError(self.T(), err) + + out = bytes.ReplaceAll(out, []byte{'\r', '\n'}, []byte{'\n'}) + fixture_name := "CAPI2_Operational" + fmt.Printf("Testing fixture %v\n", fixture_name) + + goldie.Assert(self.T(), fixture_name, out) +} + func TestEvtx(t *testing.T) { suite.Run(t, &EVTXTestSuite{}) } diff --git a/testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx b/testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx new file mode 100644 index 0000000..686234c Binary files /dev/null and b/testdata/Microsoft-Windows-CAPI2_Operational_EventID70.evtx differ