Tracking User Logon Activity Using Logon Events

I get the question fairly often, how to use the logon events in the audit log to track how long a user was using their computer and when they logged off.

As I have written about previously, this method of user activity tracking is unreliable.  It works in trivial cases (e.g. single machine where the user doesn't have physical access to the power switch or power cord), and it works most of the time in simple cases where there is good network connectivy and the user is not trying to evade detection.  If the user has physical access to the machine-- for example, can pull out the network or power cables or push the reset button-- and if the user is actively trying to evade time tracking, then the only reliable solution is to surreptitiously put a video camera (subject to local laws) in a place that can monitor the user's presence in front of the keyboard (yes I am aware of research done to track sound of keyboard clicks, etc.).

There is no way to instrument the OS to account for someone who just backs away from the keyboard and walks away.  The screen saver, if configured, will come on after a configurable delay since the last keypress or mouse movement.  Yes, if you know the SS delay then you could just work that into your calculations.  However the workstation does not lock until the screen saver is dismissed (some of you might have noticed that when you bump the mouse to dismiss the screensaver, sometimes you see your desktop for a fraction of a second- that’s because your machine isn’t locked while the screen saver is being displayed).  And the events don't tell you whether the workstation was locked or auto-locked so you don't really know whether to add in the screen saver delay factor.  Plus, prior to Windows Vista, there is no workstation lock event at all, only an unlock event, which is constructed in a way which makes it difficult to correlate with the original logon event.

So the bottom line is, I don't advocate or recommend this method for tracking the time a user spends at the keyboard.  If I were hypothetically called as an expert witness, I would testify that such a method is unreliable and trivially circumvented.  You have been warned, I've beaten that dead horse enough I guess.

Given that you are disregarding all my contrary advice, how are you going to accomplish this?

First, we need a general algorithm.

Use time (for a given logon session) = Logoff time - logon time

Now, what about the cases where the user powers off the machine, or it bluescreens, or a token leak prevents the logoff event from being generated, etc.?  We can use the BEGIN_LOGOFF event to handle token leak cases.  We can use the shutdown event in cases where the user does not log off.  And in case of crashes, the only event we can use is the startup event.  Note that each of these introduces increasing levels of uncertainty.

Logoff time = (logoff time | begin_logoff time | shutdown time | startup time)

This is good, but what about the time the workstation was locked?

Workstation lock time = unlock time - lock time
Total workstation lock time (for a given logon session) = SUM(workstation lock time)

How about remote desktop & terminal server sessions, and fast user switching?  You can connect and disconnect from logon sessions, during which time the user technically isn't using the computer.

Session idle time = session connect time - session disconnect time
Total session idle time (for a given logon session) = SUM(session idle time)

How about times when the machine was idle?  We can estimate that by looking at the time the screen saver was in place and adding the screen saver timeout.

Console idle time = (screen saver dismiss time - screen saver invoke time + screen saver delay)
Total console idle time = SUM(console idle time)

Putting all of this together and modifying our original formula, we get:

Use time (for a given logon session) =
   Logoff time - logon time
      - SUM(workstation lock time)
      - SUM(session idle time)
      - SUM(console idle time)

When we expand it, it is not quite so pretty: 

Use time (for a given logon session) =
   ( (logoff time | begin_logoff time | shutdown time | startup time) - logon time )
      - SUM(unlock time - lock time)
      - SUM(session connect time - session disconnect time)
      - SUM(screen saver dismiss time - screen saver invoke time + screen saver delay)

You have to be very careful that you only look at events that are properly contained chronologically between two other appropriate events, to avoid accidentally pairing the wrong logon and logoff events, or pairing a lock workstation event from one logon session with a different logon session.  The best correlation field is the Logon ID field, the next best are timestamp and user name.  At various times you need to examine all of these fields.

Now, which event IDs correspond to all of these real-world events?

They are all found in the Security event log.  The pre-Vista events (ID=5xx) all have event source=Security.  The Vista/WS08 events (ID=4xxx) all have event source=Microsoft-Windows-Security-Auditing.

512 / 4608  STARTUP
513 / 4609  SHUTDOWN
528 / 4624  LOGON
538 / 4634  LOGOFF
551 / 4647  BEGIN_LOGOFF

* prior to Windows Vista, there was no event for locking the workstation.  Unlocking the workstation generated a pair of events, a logon event and a logoff event (528/538) with logon type 7.  These events had the same user name as the "original" logon session and were completely enclosed chronologically by the logon/logoff events for the "real" logon session, but did not contain the Logon ID of the original logon session or other unambiguous correlator.  This makes correlation of these events difficult.

All of these events are generated in the Logon/Logoff audit policy category, although on Windows Vista and Windows Server 2008 they are scattered among the various subcategories in this audit policy category.  The audit event spreadsheet that Ned wrote has all the policy subcategory mappings as well as the event descriptions.

Sorry that this is more of a do-it-yourself than a solution-in-a-box, but this is pretty difficult to script and so far I haven't worked on a project that required this.