Diving into the new windows 11 PCA artifact
Discover the new forensic artifact in Windows 11 (22H2) and explore its format and behavior. Learn how to leverage it in investigations with our comprehensive documentation for DFIR practitioners.
In January of 2023, Andrew Rathbun and Lucas Gonzalez published a blog post showcasing a new forensic artifact that first appeared in the 2022 update of Windows 11 (22H2). In an attempt to answer additional questions that were not covered in the blog post, Sygnia’s Incident Response team decided to expand on Rathbun and Gonzalez’s research, and dive into the inner workings of this exciting new artifact.
This blog post presents our current understanding of the format and behavior of this artifact, which is based on testing and experimentation, and to some extent on reverse-engineering the PCA service binaries. We are sharing this information to provide DFIR practitioners with the necessary documentation to parse this artifact and leverage it in investigations.
The Program Compatibility Assistant (PCA) is a mechanism that was introduced in Windows Vista. PCA detects and fixes compatibility issues in legacy applications when they are executed on newer versions of Windows, and is implemented as a Windows service, called PcaSvc. This service may be familiar to you, as it is responsible for another forensic artifact: the AppCompatFlags registry key, which has existed since at least Windows 7.
In Windows 11 version 22H2, the PcaSvc service appears to store additional data within files in the
C:\Windows\appcompat\pca directory – namely,
PcaAppLaunchDic.txt, PcaGeneralDb0.txt, and
PcaGeneralDb1.txt. Our research into these files was conducted on Windows 11 version
10.0.22621.1702 (22H2) and 10.0.22632.2428 (23H2).
PcaAppLaunchDic.txt is an ANSI (CP-1252) encoded text file with Windows line endings (CRLF). Each line stores a mapping between a file’s path and its last execution time, in a pipe-delimited format. The last execution time is recorded in UTC, in the format:
YYYY-MM-DD HH:MM:SS.f, and appears to be updated consistently.
Throughout our testing, we observed that applications are only registered when executed from a GUI interface. Applications executed from the command line, by a scheduled task or remotely, using PsExec – are not registered. However, any applications that are executed from the GUI, remotely through an RDP session, from the Google Chrome downloads page, or from the 7-Zip GUI are registered.
Additionally, some built-in applications are not registered when executed from their original directory, but are registered when executed from a different directory – for example,
The fact that this file is encoded in ANSI is unexpected, because Windows supports Unicode filenames, and converting them to ANSI isn’t always possible. We executed a file whose path contains non-ANSI characters, to see how it would be recorded:
Interestingly, the associated line in the text file is truncated at the first non-ANSI character, resulting in an output of a partial file path, and no timestamp:
What’s really interesting is: it looks like executions of new files are not recorded from this point forward! The last execution times in existing lines are still updated, but new lines are no longer created in the file. This seems like a bug, rather than intended behavior, and it’s an edge case to consider when parsing this artifact.
PcaGeneralDb0.txt and PcaGeneralDb1.txt files seem to record process-related events in a log-like fashion. Both are text files encoded in Wide Character (UTF-16LE, allowing unpaired surrogates) with Windows line endings (CRLF). Each line stores a single record, and each record stores eight fields in a pipe-delimited format:
PcaGeneralDb0.txt is the primary database file, and
PcaGeneralDb1.txt is the secondary database file. New records are written to the primary file as long as it is smaller than two megabytes (2 x 106 bytes). When it reaches two megabytes, the secondary file is cleared, becomes the new primary file, and this cycle repeats. This means that between two and four megabytes of historical data are available at any given time.
As seen in the
PcaAppLaunchDic.txt file, applications executed from the command line are not registered, and some built-in applications are not registered when executed from their original location. For files executed from a UNC path, the following fields are not populated: Product Name, Company Name, Product Version, and ProgramID.
ProgramIDs were first observed in the AmCache. Blanche Lagny’s Analysis of the AmCache article includes a brief mention of the ProgramID:
“The Program Id is not yet explained, although according to the Microsoft docs, it is supposed to be a hash of the Name, the Version, the Publisher and the Language. This is consistent with the fact that the Program Id is identical across different systems: the same version of a software installed on two different machines results in the same Program Id”.
The record type field
PcaGeneralDb record type can only be between 0 and 4, and is somewhat similar to an Event ID in Windows Event Logs. Out of the five values the field can have, we were able to find the meanings of the first four:
Record type 0: Installer failed
An ‘Installer Failed’ record may be generated when an application installer is terminated in the middle of the installation process. When we modelled this scenario using a Wireshark installer, it was sometimes accompanied by a pop-up dialog:
Whether an ‘Installer Failed’ record is created for an installer appears to be dependent on the installer itself. In our testing, a record was created consistently for some installers, while for others it was consistently missing. We also observed a record for an application that isn’t an application installer at all: Disk2vhd from Sysinternals. This is yet another forensics case where “absence of evidence is not evidence of absence”.
Record type 1: Driver was Blocked
Windows 11 has mechanisms to protect against kernel memory corruption; namely, Intel’s Control-Flow Enforcement Technology (CET) and Hypervisor-Protected Code Integrity (HVCI). We assume that a ‘Driver was Blocked’ record is created when one of these mechanisms is triggered, because the record is created within a function called ‘
UddpShowBlockedDriverDialog’, and the message of this record can be one of the following:
BlockReasonCet, BlockReasonHvci or
BlockReasonOther. If this assumption is correct, this record could be extremely valuable as evidence in a Bring Your Own Vulnerable Driver (BYOVD) attack scenario.
We tried to trigger the creation of this record using EDRSandblast, a tool that uses a vulnerable signed driver to bypass security controls (such as EDR, Credential Guard and RunAsPPL). The tool was able to load and exploit the vulnerable
RTCore64.sys driver without being blocked, despite the ‘Memory Integrity’ setting and the vulnerable driver blocklist being turned on. A ‘Driver was Blocked’ record was not created in this case, presumably because no security mechanisms were triggered.
Record type 2: Abnormal Process Exit
An ‘Abnormal Process Exit’ record is created whenever an application exits with a non-zero exit code. In this case, the record message will contain the application’s exit code in hex. An ‘Abnormal Process Exit’ record can prove invaluable to determine whether an application ran successfully or not, for how long it ran, and the reason it failed.
Record Type 3: PCA Resolve is Called
The ‘PCA Resolve is Called’ records appear to be associated with the PCA mechanism itself. Their record message contains two fields: ‘resolver name’ and ‘result’. We weren’t able to identify what triggers the creation of these records, but in any case, they are clear evidence of program execution.
File Paths in PcaGeneralDb
The PCA service attempts to redact personal user information from file paths before it writes the paths to the database files. The paths appear to be lowercased, the drive letter prefix is removed, and some parts are replaced with environment variables or other placeholders. This behavior can create ambiguity. Consider this file path, which was observed in an ‘Abnormal Process Exit’ record:
Without additional context, we cannot determine the exact directory from which this file was executed. While system-wide environment variables can be resolved using the system’s registry or a predefined mapping, user-specific environment variables cannot be resolved unambiguously.
We identified two ways in which usernames are redacted from file paths before they are written to
Redaction Method 1: %USERNAME%
For every user with a profile on the system, if the file path contains the name of that user, the first occurrence of the username is replaced with the string “%USERNAME%”. For example, when we executed this file on a system, using the local user ‘Harel’:
This is how the path was registered:
Note that it doesn’t matter which user executed the file: any local or domain user who has a profile on the system will have its name replaced. If a single file path contains multiple different usernames, they will all be replaced.
If a username appears twice (or more) in a file’s path, only the first occurrence of the username is replaced.
Redaction Method 2: usersxxxxx
If a file path contains a folder named ‘Users’ (this check isn’t case-sensitive), the name of the child folder and the path separator are replaced with ‘x’ letters. We executed a file from a new ‘users’ directory inside the folder from the previous example:
This is how the path was registered:
This time, both redaction methods were used!
If a file is executed from a folder that is named ‘Users’, the filename – including the file extension – and the path separator are replaced with ‘x’ letters.
While reverse-engineering the PCA service binaries, we noticed a reference to another file,
PcaGeneralDic.txt, which did not exist on our testing machine throughout this research. We were not able to determine what causes this file to be created through testing and reverse-engineering.
Now that we have another artifact recording ProgramIDs, more research can be done on how they are computed. Once the algorithm is understood, we’ll be able to create a set of known-bad ProgramIDs and hunt for them across networks, just like we do with hash values.
Contributors: Noam Lifshitz, Oren Biderman, Amir Sadon.
To learn more about Sygnia’s Incident Response services click here.
If you are currently impacted by a cyber incident, or are seeking guidance, please contact us or call our 24/7 hotline +1-877-686-86