Fixing “SecurityHealthSystray.exe - Bad Image” (0xc000012f) When Nothing Normal Works#
I burned way more hours on this than I’d like to admit, so here’s the writeup I wish I could’ve found. TLDR up top, full story below if you want the play-by-play (or you’re stuck at a specific step).
TLDR#
Error looked like this on boot:
SecurityHealthSystray: SecurityHealthSystray.exe - Bad Image
\\?\C:\Windows\System32\SecurityHealth\10.0.29554.1001-0\SecurityHealthSSO.dll
is either not designed to run on Windows or it contains an error. Try installing
the program again using the original installation media or contact your system
administrator or the software vendor for support. Error status 0xc000012f.
None of the normal stuff fixes it — not sfc /scannow, not DISM /RestoreHealth, not even a full in-place repair install with fresh install media.
The fix that actually worked: manually replacing the corrupted DLL by mounting the drive from a Linux live environment, since Windows itself won’t let anything write to that folder.
Link to SecurityHealthSetup.exe (confirmed working as of July 2026):
https://catalog.s.download.windowsupdate.com/d/msdownload/update/software/defu/2026/01/securityhealthsetup_b0f4702a05e43a3f7389ee3ff73a37084bd8267c.exe
Steps, short version — do it in one boot:
- While still in Windows, download
SecurityHealthSetup.exefrom the link above. - Run it — right-click → Run as administrator. It won’t actually fix anything, but running it drops a copy of itself into
C:\Windows\System32\SecurityHealth. - Open that dropped copy with 7-Zip (browse it like an archive, don’t run it again), dig into
.rsrc\RCDATA\, and pull outSecurityHealthSSO.dll. - Stage the extracted DLL somewhere easy to grab once you’re in Linux — I just made a folder on the C: drive (something like
C:\SecFix) and dropped it in there. - Now boot into your recovery/live environment — I used SystemRescue.
- Mount the Windows partition:(swap in your actual partition —
sudo mkdir -p /mnt/win sudo ntfs-3g /dev/nvme0n1p3 /mnt/winlsblkto find it) - Delete the corrupted version folder (or rename it instead if you’d rather keep a backup — see the full writeup below):
cd /mnt/win/Windows/System32/SecurityHealth sudo rm -rf 10.0.29554.1001-0 - Recreate the folder and copy the DLL in from your staging folder:
sudo mkdir -p 10.0.29554.1001-0 sudo cp /mnt/win/SecFix/SecurityHealthSSO.dll 10.0.29554.1001-0/ - Unmount clean, reboot into Windows:
cd / sudo umount /mnt/win
One boot into the recovery environment, one trip, done. Full breakdown of everything I tried and why it didn’t work below, if you want the details (including the long way around, in case you land here partway through it like I did).
What the Error Actually Means#
0xc000012f is STATUS_INVALID_IMAGE_HASH. Windows is saying the file’s hash doesn’t match what it’s supposed to be — it’s corrupted, and Windows knows it, and just refuses to load it.
SecurityHealthSystray.exe is just the tray icon for Windows Security — the little shield in your taskbar. The actual protection engine (WinDefend) is a separate service and kept running fine the whole time this was broken. So this isn’t a “you’re unprotected” emergency, it’s an annoying/concerning cosmetic bug pointing at a deeper corruption issue. Still worth chasing down.
Round One: The Repair Install Didn’t Actually Fix Anything#
My first move was the standard escalation: sfc /scannow, then DISM /Online /Cleanup-Image /RestoreHealth, then — when neither did anything — a full in-place repair install using fresh Windows 11 25H2 media. That’s supposed to replace the entire Windows folder while keeping your apps and files.
Still broken afterward.
Here’s the detail that actually mattered: I went and looked at the SecurityHealth folder after the repair finished, and there were two versioned subfolders in there, both with the same old file timestamps — not the date of the reinstall. If the repair had actually written fresh copies from the install media, those timestamps would’ve updated. They didn’t. That’s proof the “repair” never touched those files at all. It just left the broken folder sitting there and, best I can tell, may have dropped a second copy alongside it without actually clearing out the original.
If you’re in this spot yourself and a full reinstall doesn’t fix a corrupted file, it’s worth ruling out bad RAM (MemTest86) or a failing drive (SMART data via CrystalDiskInfo) — that pattern can mean hardware is corrupting writes. My hardware checked out clean. The real answer here was WRP blocking the installer from overwriting the files in the first place, silently, without telling anyone it skipped something.
Round Two: Why You Can’t Just Delete the Folder Either#
Fine, I thought — if repair won’t fix it, I’ll just delete the corrupted folder and let Windows rebuild it fresh. This turned into its own fight:
- From a normal elevated Command Prompt:
ren/del→ Access Denied. takeownandicaclsboth report success, but runningicaclsagain afterward shows Administrators still only hasRX(Read & Execute), notF(Full Control). The permission change doesn’t actually stick.- That’s Windows Resource Protection doing its job — it actively defends System32 and will reassert permissions even after you’ve “changed” them.
- Booting into WinRE and trying from its command prompt doesn’t help either — WinRE runs a much thinner driver stack than a real boot, and I even hit a random unrelated error,
this stream is not DAX mappable, just trying to poke around in there. - Safe Mode gets further (Windows Security services aren’t running, so nothing has an active lock on the file) but WRP still throws Access Denied on the rename anyway.
At this point I’d tried: elevated CMD, Safe Mode, WinRE, takeown, icacls with explicit SID grants, disabling WRP via the SfcDisable registry key, and stopping SecurityHealthService outright. None of it stuck.
The Move That Actually Worked: Get Outside Windows Entirely#
WRP, TrustedInstaller ACLs, file locks — all of that is enforced by the Windows kernel. None of it exists if something else entirely is reading the disk. Boot a Linux live USB and the NTFS volume is just a filesystem to it, no opinions about which files are sacred.
Steps:
- Boot a Linux live USB — Ubuntu Live or SystemRescue, either works.
- Find your Windows partition:On NVMe it’ll look like
lsblk/dev/nvme0n1p3(note thepbefore the partition number — that’s NVMe naming, different from/dev/sda1on SATA). - Mount it:If Windows was fast-started or hibernated instead of fully shut down,
sudo mkdir -p /mnt/win sudo ntfs-3g /dev/nvme0n1p3 /mnt/winntfs-3gwill refuse and complain about an unsafe state. Force it:(If the drive’s BitLocker-encrypted,sudo ntfs-3g -o remove_hiberfile /dev/nvme0n1p3 /mnt/winntfs-3gcan’t touch it until unlocked — you’d needdislockerfor that.) - Go delete the folder (or rename it if you’d rather keep a fallback copy — up to you, I just deleted mine outright):If you’d rather be cautious and keep a backup instead of deleting:
cd /mnt/win/Windows/System32/SecurityHealth ls sudo rm -rf 10.0.29554.1001-0sudo mv 10.0.29554.1001-0 10.0.29554.1001-0.bak - Unmount clean before rebooting so you don’t leave the NTFS journal dirty:
cd / sudo umount /mnt/win - Reboot into Windows normally.
Worked instantly. No fight, no WRP, no elevation prompts. Linux just doesn’t care what TrustedInstaller thinks.
But Windows Didn’t Rebuild It On Its Own#
I figured Windows would notice the folder was gone and rebuild it on next boot. Nope — just booted up fine with an empty folder and moved on with its life.
Ran sfc /scannow, then DISM /Online /Cleanup-Image /RestoreHealth. Neither one put anything back. Not “checked and found nothing wrong” — genuinely didn’t touch that folder at all.
Turns out there’s a reason: SecurityHealthSSO.dll isn’t part of the classic servicing stack SFC/DISM check against. Confirmed it with:
dism /online /get-packages | findstr /i "SecHealth"
Came back empty. This file isn’t tracked as a DISM package — it’s part of the packaged Windows Security app (Microsoft.SecHealthUI), an AppX/MSIX deal, serviced through Windows Update, not through the normal component-store mechanism.
Dead End: Trying to Re-Register the AppX Package#
Next logical move — force Windows to re-link the app package and hope it notices the missing files and pulls them back:
Get-AppxPackage -AllUsers Microsoft.SecHealthUI
Gives you the package name (something like Microsoft.SecHealthUI_1000.278840.1000.0_x64__8wekyb3d8bbwe) and an InstallLocation. Then:
Add-AppxPackage -DisableDevelopmentMode -Register "C:\Program Files\WindowsApps\Microsoft.SecHealthUI_<version>_x64__8wekyb3d8bbwe\AppXManifest.xml"
Failed with:
Error 0x8007007E: failed to load the extension DLL due to the following error: The specified module could not be found
Checked the manifest for what it actually wanted:
Get-Content "C:\Program Files\WindowsApps\Microsoft.SecHealthUI_<version>_x64__8wekyb3d8bbwe\AppXManifest.xml" | Select-String "dll"
(Side note: if you’re copy-pasting a -Pattern command from a web page and get a weird “positional parameter cannot be found” error, it’s almost always a smart-dash getting substituted for a normal - on paste. Retype the dashes by hand, or just use the Get-Content | Select-String form above and skip named params entirely.)
Turned up SecHealthUITelemetry.dll, ViewModels.dll, and DataModel.dll — all of which were actually sitting right there in the package folder. So the failure wasn’t about those; it was almost certainly the manifest trying to register a COM/WinRT extension pointing back at the very SecurityHealth folder I’d just wiped. And you can’t just nuke the package and start over either — tried Remove-AppxPackage on it, straight Access Denied, since it’s a protected system package.
Finding the Actual Fix Tool: SecurityHealthSetup.exe#
This is the part nobody documents clearly. Microsoft has a standalone installer specifically for repairing this exact component: SecurityHealthSetup.exe. Here’s the annoying bit — despite it being distributed through the Windows Update CDN, the public Microsoft Update Catalog search (catalog.update.microsoft.com) doesn’t actually index it. Searching for it there returns zero results. The working download links that exist are direct CDN URLs people share in Microsoft Q&A/Answers threads, not something you find by searching the catalog site itself.
The one I actually used, confirmed working as of July 2026:
https://catalog.s.download.windowsupdate.com/d/msdownload/update/software/defu/2026/01/securityhealthsetup_b0f4702a05e43a3f7389ee3ff73a37084bd8267c.exe
If that’s dead by the time you’re reading this, search Microsoft Q&A / Microsoft Answers for “SecurityHealthSetup.exe” and grab a link from a recent thread — this issue comes up often enough that someone’s usually posted a current one.
Funny enough, running the exe is actually what puts a copy of itself into C:\Windows\System32\SecurityHealth — it doesn’t fix anything when you run it (WRP still blocks the actual write into the folder), but it drops itself there as a side effect. That dropped copy is what you actually want to crack open with 7-Zip in the next step.
Running it plain (right-click → Run as administrator) looked like it did nothing at all. Apparently that’s normal for some versions — it can finish silently with zero visible feedback. The more useful way to run it is pointing it directly at the target subfolder:
"C:\Windows\System32\SecurityHealth\SecurityHealthSetup.exe" "C:\Windows\System32\SecurityHealth\10.0.29554.1001-0"
Last Wall: WRP Blocks Even Microsoft’s Own Tool#
Here’s the kicker — even with the actual official repair tool in hand, it couldn’t write the file into System32 either. Same Access Denied, even fully elevated. Didn’t matter that it was Microsoft’s own signed installer. WRP doesn’t care who’s asking.
So: same trick as before. Pull the file out manually, place it via Linux. This is also the point where, in hindsight, you can save yourself a second Linux boot — extract the DLL and stage it on the C: drive somewhere easy to grab (I made a C:\fix\ folder) before going back into Linux, rather than doing what I did and bouncing back and forth. See the TLDR at the top for the one-trip version.
- Open
SecurityHealthSetup.exewith 7-Zip — treat it as an archive, don’t run it — and go to.rsrc\RCDATA\. ExtractSecurityHealthSSO.dllout to your desktop or a USB stick. - Boot the Linux live USB again.
- Mount the partition:
sudo mkdir -p /mnt/win sudo ntfs-3g /dev/nvme0n1p3 /mnt/win - Recreate the version folder (fully gone at this point since I’d deleted it earlier) and copy the DLL in:
sudo mkdir -p "/mnt/win/Windows/System32/SecurityHealth/10.0.29554.1001-0" sudo cp /path/to/SecurityHealthSSO.dll "/mnt/win/Windows/System32/SecurityHealth/10.0.29554.1001-0/" - Unmount clean, reboot:
cd / sudo umount /mnt/win
Re-enabled the SecurityHealthSystray startup entry (I’d disabled it earlier just to shut the popup up), rebooted into normal Windows — clean boot, no error. Checked Defender status the whole way through with Get-MpComputerStatus and both AntivirusEnabled and RealTimeProtectionEnabled stayed True the entire time, even while all this was broken.
Side Note: Windows.old Cleanup and OneDrive Reparse Points#
If you went the in-place repair route like I did, you’ll be cleaning up the leftover Windows.old folder afterward. One thing worth knowing ahead of time — if OneDrive’s “Files On-Demand” was on for the old profile, you may hit a folder that won’t delete, throwing:
Unsupported reparse tag 0x9000701a
That’s Microsoft’s Cloud Files API reparse tag — used by OneDrive placeholder files that show a cloud icon but aren’t actually downloaded. Regular deletion chokes on it sometimes when it’s sitting in a dead profile under Windows.old. In order of easiest to try:
- Disk Cleanup → “Clean up system files” → check “Previous Windows installation(s)” and run it. Uses proper Windows APIs, usually clears this fine.
- If that fails, force it manually:
takeown /f "C:\Windows.old\Users\<username>\OneDrive" /r /d y icacls "C:\Windows.old\Users\<username>\OneDrive" /grant administrators:F /t rmdir /s /q "C:\Windows.old\Users\<username>\OneDrive" - If
rmdirstill won’t budge, PowerShell’sRemove-Itemsometimes works wherermdirdoesn’t:Remove-Item "C:\Windows.old\Users\<username>\OneDrive" -Recurse -Force - Still stuck? Same Linux trick applies — reparse points are a Windows-only concept, so Linux just deletes it as a plain folder.
Everything I Tried, Ranked From Useless to Actually Working#
Didn’t work:
sfc /scannow, repeatedly — never touched the folder, wrong servicing mechanism entirelyDISM /Online /Cleanup-Image /ScanHealthand/RestoreHealth— same reason, confirmed bydism /online /get-packagesshowing no SecHealth package at all- Full in-place repair install with fresh 25H2 media — timestamps proved the files were never actually rewritten
- Deleting/renaming the folder from elevated CMD in a normal boot — Access Denied
- Same from WinRE — plus a random DAX-mappable error from the thin driver stack
- Same from Safe Mode — still Access Denied
takeown+icaclswith explicit SID grants — reported success, didn’t actually stick- Disabling WRP via the
SfcDisableregistry key - Re-registering the
Microsoft.SecHealthUIAppX package —0x8007007E - Removing that AppX package outright — Access Denied, protected package
- Running the actual
SecurityHealthSetup.exerepair tool from inside Windows, fully elevated — same Access Denied writing to System32
What worked:
- Linux live USB +
ntfs-3gto delete the corrupted folder out of the way. - Digging up that this component actually ships via its own standalone installer (
SecurityHealthSetup.exe) rather than through SFC/DISM/AppX. - Extracting the real DLL out of that installer with 7-Zip since the installer itself couldn’t write it into place.
- Booting Linux a second time to manually drop the DLL into the recreated folder.
- Reboot — clean.
Takeaway#
Two separate problems, stacked, both needing the same workaround. First, this file isn’t serviced by any of the tools people reach for first — it’s got its own separate, barely-documented installer. Second, once WRP decides a System32 path is off-limits, it doesn’t matter who’s asking — you, icacls, AppX registration, even Microsoft’s own signed repair tool all get the same Access Denied. The one thing that cuts through both is stepping outside Windows entirely. A Linux live USB has zero opinions about what TrustedInstaller thinks belongs where — it just does the file operation and gets out of your way.
