Search code examples
javajnaevent-log

JNA parse description strings from windows event log record


i'm using JNA to read some event logs delivered by my application. Im mostly interested in the description strings data.

I'm using the code below:

private static void readLog() {
        Advapi32Util.EventLogIterator iter = new Advapi32Util.EventLogIterator("Application");
        while (iter.hasNext()) {
            Advapi32Util.EventLogRecord record = iter.next();

            System.out.println("------------------------------------------------------------");
            System.out.println(record.getRecordNumber()
                    + ": Event ID: " + record.getInstanceId()
                    + ", Event Type: " + record.getType()
                    + ", Event Strings: " + Arrays.toString(record.getStrings())
                    + ", Data: " + record.getRecord().toString());
            System.out.println();
        }
    }

Example event my application produces:

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>
        <Provider Name="Microsoft-Windows-MyApp" Guid="{4d5ae6a1-c7c8-4e6d-b840-4d8080b42e1b}" />
        <EventID>201</EventID>
        <Version>0</Version>
        <Level>2</Level>
        <Task>2</Task>
        <Opcode>30</Opcode>
        <Keywords>0x4010000001000000</Keywords>
        <TimeCreated SystemTime="2021-02-19T15:16:03.675690900Z" />
        <EventRecordID>3622</EventRecordID>
        <Correlation ActivityID="{e6ee2b3b-9b9a-4c9d-b39b-6c2bf2550000}" />
        <Execution ProcessID="2108" ThreadID="8908" />
        <Channel>Microsoft-Windows-MyApp/Operational</Channel>
        <Computer>computer</Computer>
        <Security UserID="S-1-5-20" />
    </System>
    <UserData>
        <EventInfo xmlns="aag">
            <Username>username</Username>
            <IpAddress>127.0.0.1</IpAddress>
            <AuthType>NTLM</AuthType>
            <Resource />
            <ConnectionProtocol>HTTP</ConnectionProtocol>
            <ErrorCode>23003</ErrorCode>
        </EventInfo>
    </UserData>
</Event>

Other event UserData:

<UserData>
    <EventInfo xmlns="aag">
        <Username>otherUserName</Username>
        <IpAddress>10.235.163.52:50427</IpAddress>
    </EventInfo>
</UserData>

JNA provides event log records in EVENTLOGRECORD class which only contains methods to get only values of description strings. If i could get the record in XML format my problem would be gone. Data in UserData is not always the same, it contains different values depending on the event type. I want to parse the data from UserData section to POJO (it can be just one POJO containing all available fields). I dont want to use fields order, because some events have different fields than other (as shown in example).

Is there any way to do this using xml tag names? I will consider even switching to other lang.


Solution

  • As I pointed out in my comment you need to render the event to get to the XML. Matthias Bläsing also pointed out that some sample code is available in the WevtapiTest test class in JNA.

    Using that test class, I created the below program which reads the XML from the latest 50 events from the System log. Filtering events to what you want is left as an exercise for the reader.

        public static void main(String[] args) {
            EVT_HANDLE queryHandle = null;
            // Requires elevation or shared access to the log.
            String path = "C:\\Windows\\System32\\Winevt\\Logs\\System.evtx";
    
            try {
                queryHandle = Wevtapi.INSTANCE.EvtQuery(null, path, null, Winevt.EVT_QUERY_FLAGS.EvtQueryFilePath);
    
                // Read 10 events at a time
                int eventArraySize = 10;
                int evtNextTimeout = 1000;
                int arrayIndex = 0;
                EVT_HANDLE[] eventArray = new EVT_HANDLE[eventArraySize];
                IntByReference returned = new IntByReference();
    
                while (Wevtapi.INSTANCE.EvtNext(queryHandle, eventArraySize, eventArray, evtNextTimeout, 0, returned)) {
    
                    Memory buff;
                    // This just needs to be 0. Kept same name from test sample
                    IntByReference propertyCount = new IntByReference();
                    Winevt.EVT_VARIANT evtVariant = new Winevt.EVT_VARIANT();
                    for (int i = 0; i < returned.getValue(); i++) {
                        buff = WevtapiUtil.EvtRender(null, eventArray[i], Winevt.EVT_RENDER_FLAGS.EvtRenderEventXml,
                                propertyCount);
                        // Output the XML!
                        System.out.println(buff.getWideString(0));
                    }
                    arrayIndex++;
                    // Quit after 5 x 10 events
                    if (arrayIndex >= 5) {
                        break;
                    }
                }
                if (Kernel32.INSTANCE.GetLastError() != WinError.ERROR_SUCCESS
                        && Kernel32.INSTANCE.GetLastError() != WinError.ERROR_NO_MORE_ITEMS) {
                    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
                }
            } finally {
                if (queryHandle != null) {
                    Wevtapi.INSTANCE.EvtClose(queryHandle);
                }
            }
        }