DISM and SFC

The Component Store [%WinDir%\WinSxS] maintains a backup copy of all Windows system files, and SFC [System File Checker] & DISM [Deployment Image Servicing and Management] manage two separate, vital pieces of the Component Store and OS, with SFC relying entirely upon what DISM manages:

  • DISM has two functions SFC relies upon, /StartComponentCleanup and /RestoreHealth, with /RestoreHealth relying solely upon /StartComponentCleanup
    • /StartComponentCleanup: Cleans the Component Store of any broken hard links
      (It’s imperative folks on Insider Builds run this regularly due to the frequent updates)
    • /RestoreHealth: Verifies and fixes any corruption in the Component Store by verifying it’s system file backups against known good copies from the Windows Update servers through hash comparison; while an offline method does exist [below], it may not always fix the corruption
      • Windows 7: SUR [System Update Readiness] tool is used in lieu of this, as DISM didn’t have this functionality until Windows 8, with SUR operating differently than DISM
  • SFC always assumes the Component Store is not corrupted and is why the DISM/RestoreHealth parameter should always be run prior to SFC; not doing so allows a corrupted Component Store to potentially replace a good system file with a corrupted one or fail to fix corruption within %WinDir% altogether
    • /ScanNow: Verifies and fixes any corruption within %WinDir% by verifying against the known good copies within the Component Store through hash comparison

SFC and DISM will not resolve hardware related issues, as they only resolve OS system file corruption, excluding Registry hives and user config files within %WinDir%\System32\drivers\etc.



DISM and SFC must be executed in the order listed:
(Each relies upon what the preceding does; if Windows 7: skip to #3)

  1. Online [booted to Windows]:
    • Dism /Online /Cleanup-Image /StartComponentCleanup
    • Offline [mounted WIM, possibly when booted to a different Windows install]:
    • Dism /Image:"Z:\Windows" /Cleanup-Image /StartComponentCleanup
    • The Component Store should always be cleaned prior to running Windows Update, after an issue with Windows Update, and at least once a month, as it becomes dirty over time from updates occasionally breaking hard links.
  2. Online [booted to Windows]:
    • Dism /Online /Cleanup-Image /RestoreHealth
    • Offline [mounted WIM, or when booted to WinPE/WinRE or different Windows install]:
    • Dism /Image:"Z:\Windows" /Cleanup-Image /RestoreHealth
    • Requires an internet connection, else the offline method will be required:
      • Use the install.<esd|wim> from the Windows Install ISO for the installed version:
        1. Create Windows <#> installation media → Download tool now → install on another PC
        2. Mount ISO to determine installed OS index [image] from its install.<esd|wim>:
          • Dism /Get-ImageInfo /ImageFile:"Z:\sources\install.<esd|wim>"
        3. Specify index number at the end of the /Source parameter:
          • Online [booted to Windows]:
          • ESD: Dism /Online /Cleanup-Image /RestoreHealth /Source:esd:"Z:\sources\install.esd":6 /LimitAccess
          • WIM: Dism /Online /Cleanup-Image /RestoreHealth /Source:wim:"Z:\sources\install.wim":6 /LimitAccess
          • Offline [mounted WIM, or when booted to WinPE/WinRE or different Windows install]:
          • Dism /Image:"Z:\Windows" /Cleanup-Image /RestoreHealth /Source:esd:"Z:\sources\install.esd":6 /LimitAccess
  3. Windows 7: Run the SUR tool
  4. Reboot; if errors are found, review %WinDir%\Logs\DISM\dism.log from the bottom up
    (Log files are easier to read and sift through via the Log syntax in VS Code)
    • Windows ≥8: %WinDir%\Logs\DISM\dism.log
    • Windows 7: %WinDir%\Logs\CBS\CheckSUR.log
      (How to fix SUR errors)
  5. Online [booted to Windows]:
    • Sfc /ScanNow
    • Offline [booted to WinPE/WinRE or different Windows install]:
      • Sfc /ScanNow /OffBootDir=Z:\ /OffWinDir=Z:\Windows
    • C: is usually not the drive letter in WinPE/WinRE
    • To ascertain: DiskPart → Lis Vol → Exit
  6. Reboot; if errors are found, output to %UserProfile%\Desktop\SFCdetails.log and review:
  7. Cmd:
    • FindStr /c:"[SR]" "%WinDir%\Logs\CBS\CBS.log" > "%UserProfile%\Desktop\SFCdetails.log"
  8. PowerShell:
    • FindStr /c:"[SR]" "$env:WinDir\Logs\CBS\CBS.log" > "$env:UserProfile\Desktop\SFCdetails.log"


I run these weekly via Task Scheduler to help prevent random issues from occurring:

  1. Dism_ComponentCleanup.xml
    Executes weekly on Sundays at 11:30:00
  2. Dism_RestoreHealth.xml
    Executes weekly on Sundays at 12:00:00
  3. Sfc_ScanNow.xml
    Executes weekly on Sundays at 13:00:00

Import into Task Scheduler:

  • GUI:
    1. +R → Open: TaskSchd.msc
    2. Action → New Folder… → Name: Custom
    3. Action → Import Task… → <task_name>.xml
  • CLI:
    • Cmd:
      • SchTasks /Create /Xml "%UserProfile%\Downloads\<task_name>.xml" /Tn "\Custom\Task Name" /Ru "%ComputerName%\%UserName%"
    • Powershell:
      • Register-ScheduledTask -Xml (Get-Content '$env:UserProfile\Downloads\<task_name>.xml' | Out-String) -TaskName "Task Name" -TaskPath "\Custom\" -User $env:ComputerName\$env:UserName –Force

Using Powershell to get installed Edge and Chrome extensions

I want to start off with acknowledging that I know, but don’t have the ability, to manage Google Chrome in an organizational setting through Google Cloud which produces nice reports of extensions installed — and this can even be done with M365 Defender. My dilemma is that for some networks I do not have the ability to do this, so I needed an alternative way. This is what I’m developing.

I found an article on Spiceworks that someone created a PowerShell script already while I was trying to find the original article I got the inspiration from. I learned a little about how Chrome extensions use manifest.json and that “name” key doesn’t always, as of Manifest V2, give the actual extension name.

Reading through Chrome.18n documentation on how the manifest is laid out and internationalization structure, I was able to figure out a way to programmatically look things up.

In manifest.json the following keys are required:

default_locale is required. It can be any value, really, but standards are en_US, en, ru, etc.

name is required. It can be the actual name of the extension, but in Manifest V3, it is the string to translate which is preceded by __MSG_ and ends with __ (two underscores). The value between could be anything, for example __MSG_name__ or __MSG_AppName__.

So my thought is to look up the name key, check for __MSG_ and also the default_locale if the name key matches. This then lets me find messages.json which I can use the __MSG_name__ to find the actual extension name.

Disclaimer: This is my script so far; my PowerShell sucks. But it works for me. I do know I need to handle and log errors (unavailable computers, etc.) and not rely on -ErrorAction SilentlyContinue.

October 2024: I just realized this does not capture Developer mode unpacked/packed extensions that may be installed.

# Ensure necessary modules are loaded
Import-Module ActiveDirectory

# Define an array to store the extensions
$extensions = @()

# Define a hash table of browsers/paths
$browsers = @{
    "Edge" = "AppData\Local\Microsoft\Edge\User Data\Default\Extensions"
    "Chrome" = "AppData\Local\Google\Chrome\User Data\Default\Extensions"
}

Clear-Host

# Get a list of AD computers (handling domain and local environments)
try {
    $computers = Get-ADComputer -Filter * -Credential (Get-Credential)
} catch {
    Write-Host "Could not retrieve AD computers. Running on local machine."
    $computers = @([pscustomobject]@{ Name = $env:COMPUTERNAME })
}

foreach ($computer in $computers) {
    $computerName = $computer.Name
    $computerPath = "\\$computerName\c$\Users"

    Write-Host "******************** $computerName **********************"

    # Get a list of users excluding 'Public' and 'Default'
    try {
        $users = Get-ChildItem -Path $computerPath -ErrorAction SilentlyContinue |
                 Where-Object { $_.PSIsContainer -and $_.Name -notmatch '^(Public|Default|All Users|Default User|defaultuser0|WDAGUtilityAccount)$' }
    } catch {
        Write-Host "Failed to access users on $computerName. Skipping."
        continue
    }

    foreach ($user in $users) {
        $userName = $user.Name
        Write-Host "Checking $userName : $computerName`n"

        foreach ($browser in $browsers.Keys) {
            $browserPath = $browsers[$browser]
            $extensionPath = Join-Path -Path $computerPath -ChildPath "$userName\$browserPath"

            # Check if the Extensions folder exists
            if (Test-Path -Path $extensionPath -ErrorAction SilentlyContinue) {
                $manifestFiles = Get-ChildItem -Path $extensionPath -Filter manifest.json -Recurse -ErrorAction SilentlyContinue

                foreach ($manifestFile in $manifestFiles) {
                    $extDir = $manifestFile.DirectoryName
                    $extID = $manifestFile.Directory.Parent.Name
                    $manifestData = Get-Content -Path $manifestFile.FullName | ConvertFrom-Json

                    $extensionName = if ($manifestData.name -like "*__MSG_*") {
                        $msgJSON = Join-Path -Path $extDir -ChildPath "_locales\$($manifestData.default_locale)\messages.json"
                        $extNameKey = $manifestData.name -replace "__MSG_", "" -replace "__", ""
                        (Get-Content -Encoding UTF8 -Path $msgJSON | ConvertFrom-Json).$extNameKey.message
                    } else {
                        $manifestData.name
                    }

                    $extensions += [PSCustomObject]@{
                        ComputerName  = $computerName
                        UserName      = $userName
                        ExtensionName = $extensionName
                        ExtensionID   = $extID
                        Browser       = $browser
                    }
                    Write-Host "[$browser]: $extensionName ($extID)"
                }
                Write-Host "`n"
            }
        }
    }
}

# Export the extensions to a CSV file
$extensions | Export-Csv -Path "browser_extension_report.csv" -Encoding UTF8 -NoTypeInformation

Once it is done, a report should be created as a CSV. Here’s an example. It lists each extension installed for every user per machine scanned.

Managing Extensions in Google Chrome

What I’m working on now is a way to uninstall these after I review installed extensions and determine which ones I want to remove and block from re-installation. This is still a work in progress, here are some of my notes.

This is for removing an app, not an extension. See: Extension and App Types

"C:\Program Files\Google\Chrome\Application\chrome.exe" --profile-directory=Default --uninstall-app-id=EXTENSION_ID;

I’m also seeing some references to registry locations.

HKEY_USERS\Group Policy Objects\Machine\Software\Policies\Google\Chrome\ExtensionInstallForcelist
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist

Looks like I need to add the extension to the ExtensionInstallForceList and then remove it.

Add ForceInstall

New-ItemProperty -Path "HKLM\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist " -Name "1" -Value EXTENSION_ID

Remove ForceInstall

Note: Need to specify the same value that was added in the Add step. This does not actually remove the extension if it is installed.

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist" -Name "EXTENSION_NAME"

Remove (uninstall) the Extension

Could it be as simple as deleting the actual extension folder? sigh

Remove-Item -Path "C:\users\username\appdata\local\google\chrome\user data\default\extension\EXTENSION_ID" -Recurse

Block Extension

Use * for the EXTENSION_NAME and value of 1 to block all extension installations.

Note: -Name is an integer value being placed in the registry. To add another blocked extension, make sure to increment to the another value.

New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallBlocklist" -Name "1" -Value EXTENSION_ID;

Allow Extension

New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallAllowlist" -Name "1" -Value EXTENSION_ID;

I’ll end up doing this by Group Policy instead of per-computer in Active Directory environments. I’ll block all, and permit only approved extensions.

Group Policy (Google Policy Templates): Computer Configuration > Administrative Templates > Class Administrative Templates > Google > Chrome > Extensions

Windows Resource Protection could not perform the requested operation.

Running an sfc /scannow on a Windows Server 2019 Standard server, at about 76% it failed with the following message.

Windows Resource Protection could not perform the requested operation.
Edit

Looking through %Windir%\Windows\Logs\CBS.log I see the following:

2023-08-17 15:34:41, Error                 CSI    00004fa8 (F) c0000011 [Error,Facility=(system),Code=17 (0x0011)] #38088700# from Windows::Rtl::SystemImplementation::DirectFileSystemProvider::SysReadFile(h = cd4 ('\Device\HarddiskVolume2\Windows\WinSxS\amd64_windows-defender-management-powershell_31bf3856ad364e35_10.0.17763.831_none_5892c02f26f780e5\MSFT_MpComputerStatus.cdxml'), evt = 0, apcr = NULL, apcc = NULL, iosb = @0x45dec7c050, data = {l:0 b:}, byteoffset = (null), key = (null))
[gle=0xd0000011]
2023-08-17 15:34:41, Error                 CSI    00004fa9@2023/8/17:19:34:41.397 (F) onecore\base\wcp\sil\ntsystem.cpp(3610): Error c0000011 [Error,Facility=(system),Code=17 (0x0011)] originated in function Windows::Rtl::SystemImplementation::DirectFileSystemProvider::SysReadFile expression: (null)
[gle=0x80004005]
2023-08-17 15:34:41, Info                  CBS    Could not get active session for current session file logging [HRESULT = 0x80004003 - E_POINTER]
2023-08-17 15:34:41, Info                  CBS    Could not get file name for current session file logging [HRESULT = 0x80004003 - E_POINTER]
2023-08-17 15:34:41, Info                  CBS    Added C:\Windows\Logs\CBS\CBS.log to WER report.

Event Viewer details three events:

Fault bucket 1793356441015391089, type 5
Event Name: WindowsWcpOtherFailure3
Response: Not available
Cab Id: 0

Problem signature:
P1: 10.0.17763.4640:3
P2: wcp\sil\ntsystem.cpp
P3: Windows::Rtl::SystemImplementation::DirectFileSystemProvider::SysReadFile
P4: 3610
P5: c0000011
P6: 0x435f651d
P7:
P8:
P9:
P10:

Attached files:
\\?\C:\Windows\Logs\CBS\CBS.log
\\?\C:\Windows\Logs\CBS\CbsPersist_20230817123621.log
\\?\C:\Windows\Logs\CBS\CbsPersist_20230816233716.log
\\?\C:\Windows\Logs\CBS\CbsPersist_20230815183505.log
\\?\C:\Windows\Logs\CBS\CbsPersist_20230814052711.log
\\?\C:\Windows\Logs\CBS\CbsPersist_20230812022642.cab
\\?\C:\Windows\servicing\Sessions\Sessions.xml
\\?\C:\Windows\WinSxs\poqexec.log
\\?\C:\Windows\Logs\Cbs\FilterList.log
\\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERB67.tmp.WERInternalMetadata.xml
\\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERBA6.tmp.xml
\\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERC42.tmp.csv
\\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERC62.tmp.txt
\\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERC92.tmp.mdmp
\\?\C:\ProgramData\Microsoft\Windows\WER\ReportQueue\Critical_10.0.17763.4640__7ecf3189d0be67147e71f883985df1bed431e6e_00000000_cab_17b40ef1\memory.hdmp
\\?\C:\Windows\Temp\WEREF4.tmp.WERDataCollectionStatus.txt

These files may be available here:
\\?\C:\ProgramData\Microsoft\Windows\WER\ReportArchive\Critical_10.0.17763.4640__7ecf3189d0be67147e71f883985df1bed431e6e_00000000_10e8155a

Analysis symbol:
Rechecking for solution: 0
Report Id: b33a6f7e-74bf-456a-a60e-9af2f5c88c59
Report Status: 268435456
Hashed bucket: 8f95577d69101277c8e3482e76619371
Cab Guid: 0

Slow Performance VMware Workstation 17.0.x – Windows 10 / Windows 11

Noticed horrible performance using VMware Workstation 17 on my system. I was running Hyper-V side-by-side, so I decided to nuke Hyper-V and the subsystems from that.

  1. Removed the Hyper-V, Virtual Machine Platform, and Windows Hypervisor Platform from Windows Features.
  2. Checked to see if Memory Core Isolation was disabled, and it was. Start > Core Isolation
  3. Disabled power throttling for the VMware process:
powercfg /powerthrottling disable /path "C:\Program Files (x86)\VMware\VMware Workstation\x64\vmware-vmx.exe"
  1. Turned off ULM/Hyper-V mode
bcdedit /set hypervisorlaunchtype off
  1. Disabled Accelerated 3D Graphics in the VM settings in VMware Workstation 17.0.2.

Step 5 was the winner, for me.

I had noticed before that my GPU (Intel UHD 630 Graphics) was pegged 80%+ when attempting to work with a VMware Workstation 17.0.x virtual machine. I never put the two together. You can add the following configuration value, mks.enable3d = "FALSE", to your .vmx file, or you can edit the VM and uncheck Accelerate 3D Graphics in the Display portion of the VM configuration in VMware Workstation.

Change Windows network share permissions from command line

Grant-SmbShareAccess -Name ShareName -AccountName Administrators -AccessRight Full -Force
Grant-SmbShareAccess -Name "Brother DW2710 series" -AccountName Everyone -AccessRight Change -Force

It appears that there is no pre-existing command line tool for managing permissions on existing shares beyond the initial setup. However, you can accomplish multiple grants if temporarily taking the shares offline is not a concern. In such a case, you have the option to utilize the following approach:

NET SHARE ShareName /DELETE /Y
NET SHARE ShareName=C:\TempShare /GRANT:Everyone,Change /GRANT:Administrators,Full /UNLIMITED