1

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.

  • Does this answer your question? [Parse XML TO JAVA POJO in efficient way](https://stackoverflow.com/questions/14789302/parse-xml-to-java-pojo-in-efficient-way) – Daniel Widdis Feb 23 '21 at 05:51
  • Unfortunatly no, because events from Windows event log are not provided in XML format by JNA liblary. They are stored in a class representing single log record named `EVENTLOGRECORD`. If i could get the whole record in xml form i would parse it without any problem. The problem there is i can't read it as a XML. – Mateusz Maciejko Feb 23 '21 at 08:31
  • Ah, thanks for clarifying. I think you need to [render the event](https://learn.microsoft.com/en-us/windows/win32/wes/rendering-events) to get the XML form. That may require mapping additional WinAPI functions beyond what is already mapped in JNA. JNA does have a `getStrings()` method on the `EventLogRecord` class that gives an unstructured string array that might be helpful for some use cases. – Daniel Widdis Feb 23 '21 at 19:28
  • 2
    Actually having a quick look at the JNA platform code base, i found `EventLogRecord` and even a wrapped `Util` version, that handles memory allocation. A look at com.sun.jna.platform.win32.WevtapiTest.testReadEvents() might even help more. – Matthias Bläsing Feb 23 '21 at 21:06
  • Ah, I had missed that separate Wevtapi set of mappings. That test class does look like exactly what is needed. – Daniel Widdis Feb 23 '21 at 22:34

1 Answers1

1

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);
            }
        }
    }
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63