Installed software and the Uninstall keys: the RegRipper uninstall plugin
8 min read
The installed software inventory is one of the first things you build on any host, and the Uninstall keys in the SOFTWARE hive are where you build it from. RegRipper's uninstall plugin (and the installed_software variant on this site) walks those keys and hands you a table of what was installed, by whom, and roughly when. The catch that bites analysts is the one this post is built around: on a 64-bit Windows host there are two Uninstall trees, not one, and a tool that reads only the first will silently miss every 32-bit program on the box. Read both, including the WOW6432Node path, every single time.
Where the data lives
The 64-bit (and architecture-native) entries live under:
SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\<product>
The 32-bit-on-64-bit entries — anything a 32-bit installer wrote on a 64-bit OS — live under the WOW6432Node redirection:
SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\<product>
The WoW64 subsystem redirects 32-bit processes that touch ...\CurrentVersion\Uninstall to the WOW6432Node copy, so a 32-bit installer never sees the native tree at all. The two trees are genuinely independent. A 64-bit-only inventory shows you the OS and the 64-bit applications and quietly drops a pile of 32-bit utilities — exactly the category a lot of attacker tooling and dual-use kit still ships in.
The installed_software plugin on this site reads both, concatenating the native tree and the WOW6432Node tree into one table. That is the behaviour you want.
Each <product> subkey is one entry
Under either Uninstall path you get a set of subkeys. The subkey name is either the product's MSI ProductCode GUID (for example {90160000-008C-0000-1000-0000000FF1CE}) or a vendor-chosen string (for example Mozilla Firefox 126.0). The subkey name alone is not the display name; you read the values inside.
The plugin iterates the subkeys and, for each one that has a DisplayName value, emits a row. The DisplayName gate matters: subkeys without a DisplayName are skipped, which is the same heuristic RegRipper uses and the same one the Programs and Features UI uses. Components, patches, and update entries frequently lack a DisplayName (or carry SystemComponent=1), and you do not want them cluttering the inventory.
The values that matter:
- DisplayName — the human-readable product name. This is the gate and the first column.
- DisplayVersion — the version string as the installer recorded it. Useful for matching against a vulnerable-version list.
- Publisher — the vendor string. Free text, vendor-controlled, trivially spoofable, but a fast triage signal.
- InstallDate — the install date as an
YYYYMMDDstring (for example20260514). Note the format: it is not a FILETIME and not a Unix epoch. It is a date string with no time component, so it is day-granular at best. Some installers omit it entirely. - InstallLocation — the install directory, when the installer bothered to write it. Plenty leave it blank. The path is where you pivot next to pull the actual binaries.
The installed_software plugin here surfaces four columns — Name, Version, Publisher, and Install date — mapped from DisplayName, DisplayVersion, Publisher, and InstallDate respectively. It does not currently emit InstallLocation as a column, so if you need the on-disk path, read the subkey directly in the tree view; the value is present in the hive even when the table does not show it. (RegRipper's own uninstall.pl similarly prints a compact line per entry and does not surface every value.)
InstallDate is the day; the key LastWrite is the moment
This is the single most useful refinement to make to the raw inventory, and most ad-hoc parsers skip it.
InstallDate gives you YYYYMMDD — a calendar day, no time, and only as accurate as whatever the installer chose to stamp. For a precise install or modify timestamp, read the LastWrite time of the <product> subkey itself. Every Registry key carries a FILETIME LastWrite, updated whenever a value under it changes. When an installer creates the Uninstall subkey and populates its values, that write sets the LastWrite to the install moment, accurate to within the resolution of the FILETIME — seconds, in practice.
So:
InstallDate=20260514tells you the day.- The subkey LastWrite =
2026-05-14 09:42:17 UTCtells you the moment, and lets you order installs that happened on the same day.
Treat the two as a pair. When they agree on the date, you have a clean install with nothing touched since. When InstallDate is one day and the LastWrite is weeks later, something rewrote a value under the key after install — a repair, an in-place upgrade, or a modification. When InstallDate is missing entirely, the LastWrite is the only install-time signal you have, and it is the better one anyway. Build your install timeline from the LastWrite, not from InstallDate.
Standard caveat: the LastWrite reflects the last write, not necessarily the first. A later repair moves it forward and overwrites the original install moment in the live hive. VSS snapshots and the unallocated cells in the hive are where you recover the earlier state if you need it.
The MSI products view
There is a second, parallel inventory under the Installer subtree:
SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\
RegRipper exposes this through a separate plugin (commonly installed_products / the installer family), drawing from the MSI installer database rather than the Uninstall keys. The product and component GUIDs there are packed into the obfuscated form MSI uses, and the data overlaps the Uninstall view but is not identical: MSI-installed products appear in both, while non-MSI installers (NSIS, Inno Setup, custom bootstrappers) appear only under Uninstall. Cross-reference when an entry looks odd — a product in the MSI tables but absent from Uninstall, or the reverse, is worth a closer look. (At the time of writing, the parser on this site focuses on the Uninstall-based installed_software view; treat the MSI products view as the RegRipper-side complement and verify GUID decoding before relying on it.)
What the inventory is good for
Attacker and dual-use tooling. Anything an attacker installed through a real installer — a remote-access tool deployed as legitimate software (AnyDesk, TeamViewer, ScreenConnect), a packet capture utility, a hypervisor, a "system optimizer" that ships a driver — lands in Uninstall. The Publisher and DisplayName plus the LastWrite tell you what and when. RATs masquerading as IT software are the classic find here.
Anti-forensic and wiping utilities. CCleaner, BleachBit, Eraser, SDelete-wrapping GUIs, secure-delete tools — if they were installed rather than dropped, they register here, and the install LastWrite often sits right before the gap in your other artifacts. An anti-forensic tool installed an hour before the timeline goes dark is not a coincidence; it is a lead.
The install timeline. Sort every entry from both trees by subkey LastWrite and you get an install chronology for the host. New software appearing shortly before or during the incident window is exactly what you are hunting. Pair this with the OS install date from winver to bracket the whole picture.
Absence here does not mean absence on disk
This is the limit you must state in the report, not assume the reader knows. An empty result for a binary in Uninstall does not mean the binary was never on the system. The Uninstall keys record things installed through an installer that registered an uninstall entry. They say nothing about:
- Portable executables run from a USB stick, a download folder, or
%TEMP%. - Binaries dropped by malware and executed directly.
- Tools copied in over SMB and run in place.
- Anything
living-off-the-land, which is native and was never "installed" at all.
None of those touch the Uninstall keys. The most interesting attacker binaries on a host are frequently exactly the ones that will never appear in this inventory. So when Uninstall comes back clean for a tool you have other reasons to suspect, that is not exoneration — it is a signal to pivot to the execution and presence artifacts that do not depend on an installer.
For "was this binary ever present and did it run," go to Amcache, which records file metadata and SHA-1 hashes regardless of how the binary arrived. For the application-compatibility layer, go to Shimcache / AppCompatCache. Between Uninstall (installed software), Amcache (file presence and hashes), and Shimcache, you cover the installed and the dropped, and the gaps in one are filled by the others.
Tools
- RegRipper's
uninstallplugin (source). Walks the Uninstall keys and prints DisplayName, version, and the install date per entry. Confirm it is reading the WOW6432Node tree as well as the native one — read the plugin source to see exactly which paths and values it pulls. - The
installed_softwareview on this site. Unions the native and WOW6432Node Uninstall trees and shows Name, Version, Publisher, and Install date. Analyze a SOFTWARE hive in your browser and check the subkey LastWrite in the tree view for the precise install moment that the day-granular InstallDate cannot give you. - The RegRipper plugins reference for the rest of the SOFTWARE-hive plugin set.
The installed software inventory is fast, attribution-poor, and easy to over-trust. Read both Uninstall trees, prefer the subkey LastWrite over InstallDate for timing, cross-check against the MSI products view when something looks off, and never read absence in Uninstall as absence on disk. The binary that matters is often the one that was never installed at all.