Warning: Expect this article to be rather vague
From what I understand, when you install a program in Windows using some kind of installing package, it writes some info about the program in the registry. And that allows us to go to the control panel and see the program being listed there.
You are probably waiting for me to ask the question I seem to ask a lot here: Can we do it from command line? Good question! First candidate as the command of choice is wmic; it really allows you to get (and set) a lot of info about the computer. Let's use some examples to see what we can do: we shall begin by asking it which CPU we have
C:\Users\raub\dev> wmic cpu get name Name Intel(R) Core(TM) i5-4570T CPU @ 2.90GHz C:\Users\raub\dev>
Which bios version do we have?
C:\Users\raub\dev> wmic bios get version Version LENOVO - 14b0 C:\Users\raub\dev>What about who made the motherboard?
C:\Users\raub\dev> wmic baseboard get manufacturer Manufacturer LENOVO C:\Users\raub\dev>
From the previous command, we kinda expected this to be a Lenovo. So, let's call this verifying that we are in fact talking to a Lenovo. Now, would it tell us which motherboard is in the machine?
C:\Users\raub\dev> wmic baseboard get model Model C:\Users\raub\dev>
So it is hiding info from me. Bastard! Let's see what else we can find out about the motherboard then:
C:\Users\raub\dev> wmic baseboard get /? Property get operations. USAGE: GET [<property list>] [<get switches>] NOTE: <property list> ::= <property name> | <property name>, <property list> The following properties are available: Property Type Operation ======== ==== ========= ConfigOptions N/A N/A Depth N/A N/A Description N/A N/A Height N/A N/A HostingBoard N/A N/A HotSwappable N/A N/A InstallDate N/A N/A Manufacturer N/A N/A Model N/A N/A Name N/A N/A OtherIdentifyingInfo N/A N/A PartNumber N/A N/A PoweredOn N/A N/A Product N/A N/A Removable N/A N/A Replaceable N/A N/A RequirementsDescription N/A N/A RequiresDaughterBoard N/A N/A SKU N/A N/A SerialNumber N/A N/A SlotLayout N/A N/A SpecialRequirements N/A N/A Status N/A N/A Tag N/A N/A Version N/A N/A Weight N/A N/A Width N/A N/A The following GET switches are available: /VALUE - Return value. /ALL(default) - Return the data and metadata for the attribute. /TRANSLATE:<table name> - Translate output via values from <table name>. /EVERY:<interval> [/REPEAT:<repeat count>] - Returns value every (X interval) seconds, If /REPEAT specified the command is executed <repeat count> times. /FORMAT:<format specifier> - Keyword/XSL filename to process the XML results. NOTE: Order of /TRANSLATE and /FORMAT switches influences the appearance of output. Case1: If /TRANSLATE precedes /FORMAT, then translation of results will be followed by formatting. Case2: If /TRANSLATE succeeds /FORMAT, then translation of the formatted results will be done. PS C:\Users\mtavares\dev>
From the above, we could run
wmic baseboard get /all
to see all the properties and their current values. It does not look very pretty unless you format it (option /FORMAT), but you can see it has a lot of potential. In fact, here is a list of other interesting queries you can do in wmic.
All that is great, but what about packages since that is the subject of this article? Another very good question. Let's pick an example from the control panel and see if we can find it using wmic. The example I will use is the TightVNC, whose publisher is GlavSoft LLC:
C:\Users\raub> wmic product where "vendor like '%%glav%%'" get name Name TightVNC C:\Users\raub>
Real life example
It is real life but I changed the name to protect the guilty
Let's try it with a program I am interested on. We use a program called Careless Data, which is produced by Cargo Cult Development. According to the control panel, it consists of the following packages:
- Careless Client: The program the users run to connect to the Careless Server.
- Careless Data: Data from Careless Server that is temporarily stored in an unencrypted flat file.
- Careless Upgrades: The upgrade package that brought it to version 12.3.4 r3. Well, the Careless Client binary was also upgraded.
- Careless Dependencies: Support libraries for the Careless Client. It is actually seen as two distinct packages, CarelessDependencies and CarelessDependenciesMSI
Let's see if we can find all those packages using wmic:
C:\Users\raub> wmic product where "vendor like '%%cargo%%'" get name Name CarelessDependenciesMSI CarelessData C:\Users\raub>
Houston we have a problem: three out of five the packages are missing. What is going on here?
Enter Powershell
So I got annoyed and decided to go back to what I know more: Powershell. Short version (this article is getting long): I knew the information about installed packages was in HKLM:\Software, so I searched for a few installed packages and found some info in
- HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
- HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
Here is an example
PS C:\Users\raub> get-itemproperty -path 'HKLM:\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{09DA5EE2-7E46-4DC4-96F9-BFEE50D40659}' PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{09DA5EE2-7E46-4DC4-96F9-BFEE50D40659} PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall PSChildName : {09DA5EE2-7E46-4DC4-96F9-BFEE50D40659} PSDrive : HKLM PSProvider : Microsoft.PowerShell.Core\Registry DisplayName : Citrix Online Launcher [...] PS C:\Users\raub>
I know I did not show it above but one of the attributes we can get, besides PSPath and DisplayName, is UninstallString. so, I think we could write a script that would search those paths for programs and fetch for some attributes. Let's use the vendor name as the search criteria:
param($vendor) $locations = ("software\microsoft\windows\currentversion\uninstall", "software\Wow6432Node\Microsoft\Windows\CurrentVer sion\Uninstall") $count = 0 foreach ($location in $locations) { foreach ($obj in (get-childitem "hklm:\$location" | get-itemproperty | where {$_.publisher -match $vendor} )) { write-host "Name: $($obj.displayname)" write-host " PSChildName: $($obj.PSChildName)" write-host " PSPath $($obj.PSPath)" write-host " Publisher: $($obj.Publisher)" write-host " Version: $($obj.DisplayVersion)" write-host " BuildVersion: $($obj.Version)" write-host " UninstallString: $($obj.UninstallString)" $global:count++ } } write-host "Done" write-host " $($count) entries found"
Now let's see what we can find about our careless program:
.\findinstalledprogram.ps1 cargo Name: Careless Client 12.3.4 PSChildName: Careless Client 12.3.4_r3 PSPath Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Careless Client 12.3.4_r3 Publisher: CargoCultDevelopment LLC. Version: BuildVersion: UninstallString: "C:\Windows\unins000.exe" Name: CarelessDependenciesMSI PSChildName: {45B39321-107B-4F40-A7DC-A4CB4BFC3051} PSPath Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{45B39321-107B-4F40-A7DC-A4CB4BFC3051} Publisher: CargoCultDevelopment Version: 1.00.0000 BuildVersion: 16777216 UninstallString: MsiExec.exe /I{45B39321-107B-4F40-A7DC-A4CB4BFC3051} Name: CarelessDependencies PSChildName: {4509C469-6B21-4999-BE60-3449EE7B6EDF} PSPath Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{4509C469-6B21-4999-BE60-3449EE7B6EDF} Publisher: CargoCultDevelopment.com LLC Version: 1.00.0000 BuildVersion: 16777216 UninstallString: MsiExec.exe /I{4509C469-6B21-4999-BE60-3449EE7B6EDF} Name: CarelessUpgrades PSChildName: {E20A19AD-27C1-4F59-AED2-A04636BD2575} PSPath Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{E20A19AD-27C1-4F59-AED2-A04636BD2575} Publisher: CargoCultDevelopment LLC. Version: 12.3.4 BuildVersion: 167772240 UninstallString: MsiExec.exe /I{E20A19AD-27C1-4F59-AED2-A04636BD2575} Name: CarelessData 12.3.4 r3 PSChildName: {EA737DCD-71C9-4a06-97B5-BB2D4EE45564}_r3 PSPath Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{EA737DCD-71C9-4a06-97B5-BB2D4EE45564}_r3 Publisher: CargoCultDevelopment, LLC Version: BuildVersion: UninstallString: "C:\Program Files (x86)\CargoCultDevelopment\unins000.exe" Done 5 entries found
Back to WMIC and why you should not use it
Let's revisit using wmic again.
What is happening is that our syntax is using a WMI class, Win32_Product, that only displays products installed using Windows Installer. Now, we really should not be using Win32_Product because querying Win32_Product is quite dangerous because every time you run a query (wmic product is really a macro to wmic "select * from Win32_Product"), it will cause a consistency check of all the packages installed, leading to it trying to be helpful and trying to verify and repair the install. And that opens the door to some corruption. Microsoft suggests to use Win32reg_AddRemovePrograms, which may not show everything from wmic.
Bottom line, wmic is great but looking for package info is better done by Powershell.
No comments:
Post a Comment