// parseInsertStrings parses the insert strings from buffer which should contain // an eventLogRecord. It returns an array of strings (data is copied and // converted to UTF-8) and an array of pointers to the null-terminated UTF-16 // strings within buffer. func parseInsertStrings(record eventLogRecord, buffer []byte) ([]string, []uintptr, error) { if record.numStrings < 1 { return nil, nil, nil } inserts := make([]string, record.numStrings) insertPtrs := make([]uintptr, record.numStrings) offset := int(record.stringOffset) bufferPtr := reflect.ValueOf(&buffer[0]).Pointer() for i := 0; i < int(record.numStrings); i++ { if offset > len(buffer) { return nil, nil, fmt.Errorf("Failed reading string number %d, "+ "offset=%d, len(buffer)=%d, record=%+v", i+1, offset, len(buffer), record) } insertStr, length, err := sys.UTF16BytesToString(buffer[offset:]) if err != nil { return nil, nil, err } inserts[i] = insertStr insertPtrs[i] = bufferPtr + uintptr(offset) offset += length } return inserts, insertPtrs, nil }
// readString reads a pointer using the reader then parses the UTF-16 string // that the pointer addresses within the buffer. func readString(buffer []byte, reader io.Reader) (string, error) { offset, err := offset(buffer, reader) if err != nil { // Ignore NULL values. if err == ErrorEvtVarTypeNull { return "", nil } return "", err } str, _, err := sys.UTF16BytesToString(buffer[offset:]) return str, err }
// FormatEventString formats part of the event as a string. // messageFlag determines what part of the event is formatted as as string. // eventHandle is the handle to the event. // publisher is the name of the event's publisher. // publisherHandle is a handle to the publisher's metadata as provided by // EvtOpenPublisherMetadata. // lang is the language ID. // buffer is optional and if not provided it will be allocated. If the provided // buffer is not large enough then an InsufficientBufferError will be returned. func FormatEventString( messageFlag EvtFormatMessageFlag, eventHandle EvtHandle, publisher string, publisherHandle EvtHandle, lang uint32, buffer []byte, ) (string, error) { // Open a publisher handle if one was not provided. ph := publisherHandle if ph == 0 { ph, err := OpenPublisherMetadata(0, publisher, 0) if err != nil { return "", err } defer _EvtClose(ph) } // Create a buffer if one was not provider. var bufferUsed uint32 if buffer == nil { err := _EvtFormatMessage(ph, eventHandle, 0, 0, 0, messageFlag, 0, nil, &bufferUsed) if err != nil && err != ERROR_INSUFFICIENT_BUFFER { return "", err } bufferUsed *= 2 buffer = make([]byte, bufferUsed) bufferUsed = 0 } err := _EvtFormatMessage(ph, eventHandle, 0, 0, 0, messageFlag, uint32(len(buffer)/2), &buffer[0], &bufferUsed) bufferUsed *= 2 if err == ERROR_INSUFFICIENT_BUFFER { return "", sys.InsufficientBufferError{err, int(bufferUsed)} } if err != nil { return "", err } // This assumes there is only a single string value to read. This will // not work to read keys (when messageFlag == EvtFormatMessageKeyword). value, _, err := sys.UTF16BytesToString(buffer[0:bufferUsed]) return value, err }
// RenderEventNoMessage render the events as XML but without the RenderingInfo (message). func RenderEventNoMessage(eventHandle EvtHandle, renderBuf []byte) (string, error) { var bufferUsed, propertyCount uint32 err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(renderBuf)), &renderBuf[0], &bufferUsed, &propertyCount) if err == ERROR_INSUFFICIENT_BUFFER { return "", sys.InsufficientBufferError{err, int(bufferUsed)} } if err != nil { return "", err } if int(bufferUsed) > len(renderBuf) { return "", fmt.Errorf("Windows EvtRender reported that wrote %d bytes "+ "to the buffer, but the buffer can only hold %d bytes", bufferUsed, len(renderBuf)) } xml, _, err := sys.UTF16BytesToString(renderBuf[:bufferUsed]) return xml, err }
// parseEventLogRecord parses a single Windows EVENTLOGRECORD struct from the // buffer. func parseEventLogRecord(buffer []byte) (eventLogRecord, error) { var record eventLogRecord reader := bytes.NewReader(buffer) // Length err := binary.Read(reader, binary.LittleEndian, &record.length) if err != nil { return record, err } if len(buffer) < int(record.length) { return record, fmt.Errorf("Decoded EVENTLOGRECORD length (%d) is "+ "greater than the buffer length (%d)", record.length, len(buffer)) } // Reserved err = binary.Read(reader, binary.LittleEndian, &record.reserved) if err != nil { return record, err } if record.reserved != uint32(0x654c664c) { return record, fmt.Errorf("Buffer does not contain ELF_LOG_SIGNATURE. "+ "The data is invalid. Value is %X", record.reserved) } // Buffer appears to be value so slice it to the adjust length. buffer = buffer[:record.length] reader = bytes.NewReader(buffer) reader.Seek(8, 0) // RecordNumber err = binary.Read(reader, binary.LittleEndian, &record.recordNumber) if err != nil { return record, err } // TimeGenerated err = binary.Read(reader, binary.LittleEndian, &record.timeGenerated) if err != nil { return record, err } // TimeWritten err = binary.Read(reader, binary.LittleEndian, &record.timeWritten) if err != nil { return record, err } // EventID err = binary.Read(reader, binary.LittleEndian, &record.eventID) if err != nil { return record, err } // EventType err = binary.Read(reader, binary.LittleEndian, &record.eventType) if err != nil { return record, err } // NumStrings err = binary.Read(reader, binary.LittleEndian, &record.numStrings) if err != nil { return record, err } // EventCategory err = binary.Read(reader, binary.LittleEndian, &record.eventCategory) if err != nil { return record, err } // ReservedFlags (2 bytes), ClosingRecordNumber (4 bytes) _, err = reader.Seek(6, 1) if err != nil { return record, err } // StringOffset err = binary.Read(reader, binary.LittleEndian, &record.stringOffset) if err != nil { return record, err } if record.numStrings > 0 && record.stringOffset > record.length { return record, fmt.Errorf("StringOffset value (%d) is invalid "+ "because it is greater than the Length (%d)", record.stringOffset, record.length) } // UserSidLength err = binary.Read(reader, binary.LittleEndian, &record.userSidLength) if err != nil { return record, err } // UserSidOffset err = binary.Read(reader, binary.LittleEndian, &record.userSidOffset) if err != nil { return record, err } if record.userSidLength > 0 && record.userSidOffset > record.length { return record, fmt.Errorf("UserSidOffset value (%d) is invalid "+ "because it is greater than the Length (%d)", record.userSidOffset, record.length) } // DataLength err = binary.Read(reader, binary.LittleEndian, &record.dataLength) if err != nil { return record, err } // DataOffset err = binary.Read(reader, binary.LittleEndian, &record.dataOffset) if err != nil { return record, err } // SourceName (null-terminated UTF-16 string) begin, _ := reader.Seek(0, 1) sourceName, length, err := sys.UTF16BytesToString(buffer[begin:]) if err != nil { return record, err } record.sourceName = sourceName begin, err = reader.Seek(int64(length), 1) if err != nil { return record, err } // ComputerName (null-terminated UTF-16 string) computerName, length, err := sys.UTF16BytesToString(buffer[begin:]) if err != nil { return record, err } record.computerName = computerName _, err = reader.Seek(int64(length), 1) if err != nil { return record, err } return record, nil }
// formatMessage takes event data and formats the event message into a // normalized format. func formatMessage( sourceName string, eventID uint32, lang uint32, stringInserts []uintptr, buffer []byte, pubHandleProvider func(string) sys.MessageFiles, ) (string, error) { var addr uintptr if len(stringInserts) > 0 { addr = reflect.ValueOf(&stringInserts[0]).Pointer() } messageFiles := pubHandleProvider(sourceName) var lastErr error var fh sys.FileHandle var message string for _, fh = range messageFiles.Handles { if fh.Err != nil { lastErr = fh.Err continue } numChars, err := _FormatMessage( windows.FORMAT_MESSAGE_FROM_HMODULE| windows.FORMAT_MESSAGE_ARGUMENT_ARRAY, Handle(fh.Handle), eventID, lang, &buffer[0], // Max size allowed is 64k bytes. uint32(len(buffer)/2), // Size of buffer in TCHARS addr) // bufferUsed = numChars * sizeof(TCHAR) + sizeof(null-terminator) bufferUsed := int(numChars*2 + 2) if err == syscall.ERROR_INSUFFICIENT_BUFFER { return "", err } if err != nil { lastErr = err continue } if bufferUsed > len(buffer) { return "", fmt.Errorf("Windows FormatMessage reported that "+ "message contains %d characters plus a null-terminator "+ "(%d bytes), but the buffer can only hold %d bytes", numChars, bufferUsed, len(buffer)) } message, _, err = sys.UTF16BytesToString(buffer[:bufferUsed]) if err != nil { return "", err } } if message == "" { switch lastErr { case nil: return "", messageFiles.Err case ERROR_MR_MID_NOT_FOUND: return "", fmt.Errorf("The system cannot find message text for "+ "message number %d in the message file for %s.", eventID, fh.File) default: return "", fh.Err } } return message, nil }