AppCompatCache Part 3

In my previous post, I went though the structure of the AppCompatCache and then parsed out the actual values found. I expect this to be the last part of this series and goes over some additional findings and thoughts. However, I'm working with Richard of 13Cubed on this, so there's the possibility that something new might come up. If it's earth shattering, I'll make a part 4. Otherwise, I'll just update this post.

There's some additional discussion on this flag in the GitHub Issues for AppCompatCacheParser that I found here: https://github.com/EricZimmerman/AppCompatCacheParser/issues/6. Zimmerman confirms that he found this via his own testing. Thus, like the insert flag, "execution" may not be the actual meaning of the values in that location. It was reverse engineered based on observation. This isn't unusual in forensics, it's just important to understand from a scientific standpoint. Much like a theory, it explains the known facts and can be used to make future predictions. Like a theory, it grows more certain as more observations are made and tested. That's part of why I'm chasing this and I think it's important that others do the same and document their own findings. It's also important to understand that new information can change the theory, so from a practical standpoint it's best to back-up any findings of "Execution" with additional artifacts.

There's also some brief discussion here https://github.com/EricZimmerman/AppCompatCacheParser/issues/9 in which Zimmerman adds that it's important to understand the artifact.

My takeaway is that it's important not to take any tools single-word column name as clear-cut ground truth. It might lead us in the right direction, but back it up with other artifacts and be aware of the limitations and caveats.

Part 1 - https://nullsec.us/windows-10-11-appcompatcache-deep-dive/
Part 2 - https://nullsec.us/building-appcompatcacheparser/

Three sentence takeaway:

I have a high degree of confidence that something marked with a Yes has been executed. I have a low degree of confidence that something marked with a No has not been executed. Now go read the rest, don't base your findings on a TL;DR.

What bytes are we looking at?

I mentioned in the Windows10.cs portion of my last post that some of you might have noticed an inconsistency in the number of bytes we are looking at in the data field.

In the original structure table from velong.io it indicated that there were 3 bytes of padding prior to the signature/start of a new record. This seemed a little odd to me though for two reasons.

  1. It's not a multiple of two. There's no reason it has to be, just that three feels like an odd number to use in a data structure.
  2. More importantly, in the PDF, these were specified as "Null Padding", but I was seeing data (non-null) bytes in a number of them.

Therefore, I assumed that this may have been a mistake, and that it should have been the last two bytes as padding, which did (at first glance) appear to be null for everything. If you look at the original code though, Zimmerman is looking at the last four bytes. Maybe this was just a shortcut, since the padding should be null?  That's what I figured, as you'll notice with the first few identifiers in my code, which are named with only the two bytes of interest. After processing the file though, I found that the "padding" (last two bytes) was not always null, e.g. 64-86-4C-01 and ⁠4C-01-64-86.

So, is there no padding here? Or is there padding and it's just not always null? If so, how many bytes is the padding? I don't have the answers here, just know that AppCompatCache parser is looking at the last 4 bytes of a record, is not considering any padding, and is looking specifically for 01 00 00 00 to determine execution.

Below I may only reference things by two bytes, e.g. 01 00. If I do, the remaining bytes are null, e.g. 01 00 00 00.

Observations

I've been playing in a VM for a bit, just trying to get a feel for the values. There's no particular starting point here except where I currently am.

I downloaded WinDirStat portable (WinDirStatPortable_1.1.2.80.4.paf.exe) and the 7zip installer (97z2408-x64.exe). WinDirStat comes in a self extracting executable. Prior to executing either of these, only viewing them in an Explorer window, I rebooted. Parsing Shimcache after shows a value of 4c 01 for both, or "No" for execution. So far correct, although I'd be interested to know exactly what 4c 01 means.

Next I installed 7zip, and then right clicked on the WinDirStatPortable executable -> Show more options -> 7-zip -> Extract to WinDirStatPortable_1.1.2.80.4.paf. Without navigating into the resulting folder, I rebooted.


Observation #1 - Execution without "Execution"

There was only one newly shimmed entry related to those activities, C:\Program Files\7-Zip\7zG.exe. This is the executable that should have been used to decompress WinDirStat, and thus should have executed. However, the bytes for this file were 00 00, or "No" for Execution. Thankfully I have Sysmon running, so I checked, looking specifically at Event ID 1, Process Create, where we can see execution.

Image: C:\Program Files\7-Zip\7zG.exe
FileVersion: 24.08
Description: 7-Zip GUI
Product: 7-Zip
Company: Igor Pavlov
OriginalFileName: 7zg.exe
CommandLine: "C:\Program Files\7-Zip\7zG.exe" x -o"C:\Users\user\Downloads\WinDirStatPortable_1.1.2.80.4.paf\" -spe -an -ai#7zMap21281:126:7zEvent23556

I also checked Prefetch, where I have 7ZG.EXE-F49B3D46.pf. So we have proof of execution, without the execution bytes being set. I can't explain this yet, except that maybe because it was via menu actions? There's more later though.


Back to other items, I do see the execution flag 01 00 set for the 7zip installer (97z2408-x64.exe) that I double clicked on. That is correct. the WinDirStat portable executable is still set to 4c 01, that is correct as well since it was extracted, not executed. I also don't see any of executables inside the WinDirStat directory since I didn't browse to them, this is expected.

I browsed to C:\Program Files\7-Zip\ and double clicked on 7zG.exe. I got an error window, but it did run. Next I browsed to the WinDirStat directory, but did not run anything. Reboot time.


Observation #2 - 7zG.exe now shows executed

That's really about it for this one. While files that are listed in command prompt won't be shimmed, files that are executed should be. Therefore the previous execution should have shimmed 7zG.exe, but didn't. After double clicking on it in Explorer though, the value has changed to 01 00.


Other changes since the last reboot:

  • Uninstall.exe from C:\Program Files\7-Zip\ was added with a value of 4c 01
  • 7zFM.exe was added with a value of 00 00
  • 7z.exe was added with a value of 64 86
    • All three of these were only viewed in the file explorer. None have an associated Prefetch file, and none of them appear in Sysmon process creation events. So it's correct that none of them would show execution, however, I'm not sure why they would each have different values.
  • There is a -new- shim entry for the WinDirStatPortable executable. I didn't modify, copy, delete, or otherwise do anything to this executable, the value (4c 01) is still the same. I only have one control set (current/001), and the Duplicate column is not checked. ¯\(ツ)/¯. I'm not sure why, but this may impact the number of unique records kept, as older records will roll off quicker if there are multiple entries for the same executable.
doubleentry

I made a copy of WinDirStatPortable.exe on the desktop, and then used a PowerShell script to copy 100 of them all into a folder named "lab2" with the naming convention 1.exe, 2.exe, 3.exe, etc. Rebooted, and as expected, no indication of any of them in the lab2 folder, but I do see an entry for the one copied to the desktop with the same value of 4c 01 seen earlier.

I opened the lab2 folder. From the Explorer window, I can see 1.exe through 19.exe. I ran 13.exe and rebooted. I'll note for anyone repeating this that I got an error for a missing ini file, but it doesn't matter, there was execution.

Nothing unexpected from this test. The behavior was exactly as expected, 13.exe has the value 01 00 indicating execution, the other 18 that were visible in the Explorer window all have 4c 01. Likewise, running 18.exe from the command line and rebooting resulted in the expected 01 00 value change to that.


Observation #3 - Firefox "private_browsing.exe" not showing execution

This is another item like 7zG.exe, where something is executed, but does not show as executed. When I provisioned this virtual machine I installed Firefox. Within C:\Program Files\Mozilla Firefox is an executable named private_browsing.exe that does what it says on the tin. I noticed that this had a value of 00 00, and wondered what would happen if I opened a private browsing window from Firefox, as you would expect someone to do. What happened is nothing, still 00 00, so I opened it from the start menu, ran it from the command line, and finally double clicked it in Explorer, rebooting after each. I did confirm that it had executed, using both Prefetch and Sysmon as I did above for 7zG.exe. Unlike 7zG.exe though, this one never changed. I did notice that, even when running that file specifically, task manager shows a command line of firefox.exe -private-window. There's no process running for private_browsing.exe. I'm not sure if that's the reason or something else. I have two examples now where something executed but would not have the execution flag set.



Observation #4 - System files (mostly) never show as executed

I'm not sure what the pattern is here, system files (mostly) never get set to 01 00, with the majority having a value of 00 00. There's all sorts of other values, just never Executed. I say mostly, because there are some exceptions. The table below shows those that don't follow this pattern (i.e. are correctly reported), at least on my system.

+----------+------------------------+----------------------------------+
| Executed | Last Modified Time UTC | Path |
+++====================+
| _0100 | 2021-06-05 12:05:58 | C:\Windows\SysWOW64\getmac.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2024-08-22 21:33:57 | C:\Windows\System32\conhost.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:06:10 | C:\Windows\system32\mmc.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:06:23 | C:\Windows\syswow64\MsiExec.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:06:10 | C:\Windows\system32\msiexec.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:06:23 | C:\Windows\SysWOW64\regsvr32.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:06:11 | C:\Windows\system32\regsvr32.exe |
+----------+------------------------+----------------------------------+
| _0100 | 2021-06-05 12:05:23 | C:\Windows\system32\dwm.exe |
+----------+------------------------+----------------------------------+

Notice that cmd.exe and PowerShell.exe do not appear in this list despite me using both repeatedly throughout this lab. Both of those show 00 00, even when copied to a new location and executed. I'm not sure how AppCompatCache keeps up with which files are "system" files, or if this is even a correct observation.

This also goes for built in Windows apps, for instance the following both show up as 00 00 after execution.

  • C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_10.2012.21.0_x64__8wekyb3d8bbwe\Application
  • C:\Program Files\WindowsApps\Microsoft.BingNews_1.0.6.0_x64__8wekyb3d8bbwe\Application

I also have entries, with no timestamp, under 64 86 after executing those.

  • 00000009 000a07dc00150000 0000000000000000 8664 Microsoft.WindowsCalculator 8wekyb3d8bbwe
  • 00000009 0001000000060000 0000000000000000 8664 Microsoft.BingNews 8wekyb3d8bbwe


Observation #5 - Modern apps

I started to get into this a bit above. A lot of these seem to be bucketed into 64 86 (though not all). Multiple entries are duplicate, some of those have different values, some of them the same. Some of them, such as Notepad, do have the execution bit set. Others, such as BingNews, do not.

I'm not sure if the naming convention is supposed to be space delimited fields, but if you look at them as such, they all have 8664 as the fourth field, ex.
00000009 07e401f7003a0000 000a00004a610000 8664 Microsoft.WindowsCamera 8wekyb3d8bbwe/. This would be 64 86 converted from little-endian (or vice versa) that Microsoft tends to use.

Looking back at our BingNews execution from #4, we can see the two new cache entries, making three total. Remember, the lower the number, the more recent the execution (e.g. 0 is the newest)

modernapps

Grouping these in buckets

Here's where things stand on my latest run, staring with things that should have been executed.

01 00 00 00 (Executed = True)

81 of 558 entries (14%) of items fall into this category.

Most of the things in here are items that I either executed, or would reasonably expect to be executed as part of system processes. As an example, items in %TEMP% that were likely executed as part of an install process and modern apps that may be used for authentication in the background or may start automatically. There is nothing in here that I can point to and say with certainty "that did not execute". If I were to testify to an item in this list, I think I could say that it executed "with a high degree of confidence". However, I would not say "with absolute certainty".

Everything else (Executed = False)

477 of 558 entries (85%) of all items fall into this bucket.

We'll break this up further below. Before I do though, anything that falls into this I think I would say that I can I only have a "low degree of confidence" (depending on the executable) that something in this bucket did NOT execute. For instance, if a case involved someone opening a Firefox private browsing window, I could not answer (based on this artifact alone) whether they did or did not. However, I would have more confidence in firefox.exe itself if asked about that one. Again though, I would never be willing to say anything with absolute certainty.

00 00 00 00

289 of 558 (58%)

There's a number of native apps in this bucket, as well as a fair number of what appears to be Edge updates. Some, though not all, of these things I would have expected to be executed. However, there's also plenty of things in here that I know were not executed.

Then there's items that I'm not sure about. For example, there's a number of binaries from C:\Program Files\Git\ (which was installed with Visual Studio). I know I never browsed to that directory. I could see them being shimmed if they were executed with Visual Studio, but if so we would have more outliers (executed items not marked as such). If not, how did they end up in Shimcache?

Also in here seems to be the majority of system files, C:\WIndows\System32, but also SystemTemp, SystemAps, SysWOW64, etc.

02 00 00 00

3 of 558 (0.5%)

All of these belong to Edge identity_helper.exe, with different version numbers in the path. e.g. C:\Program Files (x86)\Microsoft\Edge\Application\[VERSION]\identity_helper.exe

4c 01 00 00

23 of 558 (4%)

These all seem to be things that were viewed in an Explorer window but not executed, with the exception of C:\Windows\winhlp32.exe. The rest are multiple executable from the "lab2" folder, one of the 7-zip files, and a few things from Documents and Downloads. Note that this isn't everything that was viewed without being executed though.

64 86 00 00

158 of 558 (28%)

The majority of these are Windows modern apps. There's also a few things in here that I recognize as having been viewed but not run. I did have a previous "lab" folder on the desktop (thus the "lab2"). Whereas the WinDirStat viewed executables from lab2 were mostly 4c 01, the EventFinder.exe executable used for lab were mostly 64 86.

There is also 41 Windows system files in this bucket.

64 86 4c 01

0 of 558 (0%)

I know I came across this in some previous testing, but it isn't in any in the parsed files that I created since I started making notes. It may very well be the same as 4c 01 64 86. I have the feeling that each 2 bytes means something. In other words, you can have a file that is both 4c 01 and 64 86 and it wouldn't matter what order those are specified in.

4c 01 64 86

1 of 558 (0.1%)

The only entry in here is for C:\Users\user\Desktop\lab\1.exe. I don't know why, and I'm pretty sure this isn't a binary that I executed (I don't usually pick the first or last).