<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christopher Smith</title>
    <description>The latest articles on DEV Community by Christopher Smith (@et-ducky).</description>
    <link>https://dev.to/et-ducky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4138586%2F0f9ef5ba-394d-43c0-9770-70cfc3e50ec6.png</url>
      <title>DEV Community: Christopher Smith</title>
      <link>https://dev.to/et-ducky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/et-ducky"/>
    <language>en</language>
    <item>
      <title>Finding the Filter Driver Behind High CPU in Unrelated Windows Processes</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Tue, 03 Nov 2026 15:55:32 +0000</pubDate>
      <link>https://dev.to/et-ducky/finding-the-filter-driver-behind-high-cpu-in-unrelated-windows-processes-31am</link>
      <guid>https://dev.to/et-ducky/finding-the-filter-driver-behind-high-cpu-in-unrelated-windows-processes-31am</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;When a monitoring agent, powershell.exe, and Process Explorer all show high CPU at the same time, the work often isn't theirs. A kernel filter driver that inspects their file activity can be using that CPU inside their own threads. This post covers how to confirm that with a built-in counter, list the filter drivers on the machine, and find the one in the thread stacks.&lt;/p&gt;

&lt;p&gt;A recent &lt;a href="https://www.reddit.com/r/sysadmin/comments/1xjm7wx/" rel="noopener noreferrer"&gt;r/sysadmin thread&lt;/a&gt; traced high CPU on monitoring agents, powershell.exe, and Process Explorer to a tamper protection driver from the endpoint security product on those machines. The poster asked for ways to find this kind of cause without WPA and ETW traces. The first three steps below need neither.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the wrong process shows the CPU
&lt;/h2&gt;

&lt;p&gt;Microsoft's minifilter documentation says most pre-operation callbacks are called in the context of the thread that originated the I/O request. When a filter driver spends time on each file open, that time is counted against the process that opened the file. The processes with the most file activity then look like the cause, and the driver doesn't show up as a process at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare kernel time with user time
&lt;/h2&gt;

&lt;p&gt;Time a thread spends in a driver is kernel-mode time, which Windows counts as privileged time. Sample both counters for every process for 30 seconds while the CPU is high.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\Process(*)\% Privileged Time'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'\Process(*)\% User Time'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SampleInterval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxSamples&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;6&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A process that is busy with its own work mostly shows user time. When several unrelated processes show mostly privileged time at once, a driver is the likely source. Run the same command on a machine of the same build that doesn't have the problem, so you have a baseline to compare against.&lt;/p&gt;

&lt;h2&gt;
  
  
  List the filter drivers
&lt;/h2&gt;

&lt;p&gt;Fltmc.exe is the system-supplied utility for managing minifilter drivers. From an elevated prompt, list the loaded filters with their altitudes, then the volumes each one is attached to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fltmc filters
fltmc instances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft assigns each minifilter an altitude, and the altitude ranges follow load order groups. Antivirus filters are in the 320000 to 329999 range, and activity monitors that observe and report on file I/O are in 360000 to 389999. Check the filters in those ranges first, starting with any that belong to a product installed or updated around the time the CPU problem started.&lt;/p&gt;

&lt;p&gt;Find the driver file and the product that installed it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-CimInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Win32_SystemDriver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Name='&amp;lt;filter name&amp;gt;'"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;DisplayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;PathName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the properties of the file in PathName and read the company, product name, and file version on the Details tab. Write the version down for the vendor ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the thread stacks
&lt;/h2&gt;

&lt;p&gt;Process Explorer from Sysinternals shows the call stack of each thread. Install the Debugging Tools for Windows, then under Options and Configure Symbols set the dbghelp.dll path to the copy in the Debuggers folder of the Windows Kits and set the symbols path to the Microsoft symbol server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;srv*C:\symbols*https://msdl.microsoft.com/download/symbols
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Process Explorer as administrator. Open the properties of one of the busy processes, go to the Threads tab, select the thread with the most CPU, and click Stack. The stack is a snapshot, so click Refresh a few times while the CPU is high.&lt;/p&gt;

&lt;p&gt;A frame from a third-party driver shows as the .sys file name with an offset, because the Microsoft symbol server has no symbols for it. Do the same in a second process that has nothing to do with the first. The same driver in the stacks of both is the evidence to send to the vendor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm it with a trace
&lt;/h2&gt;

&lt;p&gt;When you need a record of the whole machine, Windows Performance Recorder captures CPU samples with stacks. WPR and Windows Performance Analyzer come with the Windows Performance Toolkit in the Windows ADK. Record for about a minute while the CPU is high.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\Traces
&lt;span class="nb"&gt;wpr&lt;/span&gt; &lt;span class="na"&gt;-start &lt;/span&gt;&lt;span class="kd"&gt;CPU&lt;/span&gt; &lt;span class="na"&gt;-filemode
&lt;/span&gt;&lt;span class="nb"&gt;wpr&lt;/span&gt; &lt;span class="na"&gt;-stop &lt;/span&gt;&lt;span class="kd"&gt;C&lt;/span&gt;:\Traces\highcpu.etl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open C:\Traces\highcpu.etl in WPA. On the Trace menu, click Load Symbols. Open the CPU Usage (Sampled) graph, add the Thread ID and Stack columns, and expand the stacks of the busy processes. The filter driver's .sys module appears in the stacks of each process it is slowing down, and the Weight column shows how much sampled CPU falls under it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix the cause
&lt;/h2&gt;

&lt;p&gt;Send the stacks, the driver file version, and the counter output from both machines to the vendor of the product that installed the driver. Check that vendor's release notes for a newer agent version that addresses CPU use. Don't unload a security product's filter with fltmc to test the theory on a production machine, because that removes the protection it provides.&lt;/p&gt;

&lt;p&gt;I'd test any exclusion or setting change the vendor suggests on one machine first. Run the Get-Counter command again afterward and compare privileged time with the numbers you recorded before the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its Windows agent reads the kernel event stream through ETW on every managed endpoint. You can open a Live Session shell from the browser without the need for a VPN and run the Get-Counter and fltmc commands above on the machine with the high CPU. A Live Session also runs an AI-driven investigation in which the AI picks the ETW providers for each round. Only counts-only summaries leave the host, and the raw kernel events stay on it.&lt;/p&gt;

&lt;p&gt;You'd start a Multi-Agent Session across a machine with the high CPU and one without it. It compares the kernel events from both to find the outlier. A CPU alert on that machine also gets an automatic root-cause analysis with the cause, impact, mitigation, prevention, and a confidence level.&lt;/p&gt;

&lt;p&gt;One Windows or Linux server on the Free query tier with your own AI key costs $5 a month: &lt;a href="https://etducky.com/pricing" rel="noopener noreferrer"&gt;https://etducky.com/pricing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>troubleshooting</category>
      <category>powershell</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Event ID 41 Kernel-Power After an Unexpected Restart and How to Find the Cause</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Fri, 30 Oct 2026 13:35:56 +0000</pubDate>
      <link>https://dev.to/et-ducky/event-id-41-kernel-power-after-an-unexpected-restart-and-how-to-find-the-cause-2omb</link>
      <guid>https://dev.to/et-ducky/event-id-41-kernel-power-after-an-unexpected-restart-and-how-to-find-the-cause-2omb</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;Event ID 41 from Microsoft-Windows-Kernel-Power is a Critical event in the System log. The message reads "The system has rebooted without cleanly shutting down first. This error could be caused if the system stopped responding, crashed, or lost power unexpectedly." Windows logs it at the next startup after a shutdown that didn't finish cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it means
&lt;/h2&gt;

&lt;p&gt;Event 41 records that the last shutdown wasn't clean. It doesn't record why on its own. When Windows saved a Stop error before the restart, the event carries the bug check code. When someone held the power button to force the restart, it carries a nonzero PowerButtonTimestamp. When the power was cut or the machine hung hard, the values can all be zero, or the event can be missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to find the cause
&lt;/h2&gt;

&lt;p&gt;List the restart events in the System log from an elevated PowerShell prompt. Event 1074 means an application or a user started the shutdown or restart. Event 6008 means the previous shutdown was unexpected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1074&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6008&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 1074 message names the user account, so blur it before posting the output anywhere.&lt;/p&gt;

&lt;p&gt;Read the event data of the newest event 41.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Microsoft-Windows-Kernel-Power'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToXml&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find BugcheckCode and PowerButtonTimestamp in the EventData section of the output. BugcheckCode is in decimal, and Microsoft's bug check reference lists codes in hexadecimal. A BugcheckCode of 159 converts to 0x0000009F, which is DRIVER_POWER_STATE_FAILURE.&lt;/p&gt;

&lt;p&gt;When BugcheckCode is nonzero, open the dump from that crash. Small memory dumps are in &lt;code&gt;%SystemRoot%\Minidump&lt;/code&gt;, and kernel and complete dumps are at &lt;code&gt;%SystemRoot%\MEMORY.DMP&lt;/code&gt;. Install Debugging Tools for Windows from the Windows SDK, set the WinDbg symbol path to &lt;code&gt;https://msdl.microsoft.com/download/symbols&lt;/code&gt;, open the dump, run &lt;code&gt;!analyze -v&lt;/code&gt;, then read the module named in the STACK_TEXT section.&lt;/p&gt;

&lt;p&gt;When BugcheckCode and PowerButtonTimestamp are both zero, check whether Windows could write a dump at all. Event 46 from volmgr reads "Crash dump initialization failed!" when the computer started without a configured dump file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'volmgr'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then read the System and Application logs for the half hour before the time in event 6008. Replace the two times with a window that ends at that time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Application'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;StartTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'2026-10-27 22:30'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;EndTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'2026-10-27 23:00'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LevelDisplayName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fixes
&lt;/h2&gt;

&lt;p&gt;When the bug check analysis names a driver, update that driver from the hardware vendor or roll it back to the version it was before the restarts started. Also update the BIOS and firmware.&lt;/p&gt;

&lt;p&gt;When PowerButtonTimestamp is nonzero, the restart was forced with the power button. Find what made Windows stop responding before that, and hold the power button only when the machine doesn't respond.&lt;/p&gt;

&lt;p&gt;When the values are zero, check the hardware. Disable overclocking, run a memory test and confirm every memory module runs at the same speed, confirm the power supply has enough wattage for the installed devices, and check component temperatures. Put the machine on a UPS when the restarts follow power cuts.&lt;/p&gt;

&lt;p&gt;When event 46 from volmgr is present, fix the pagefile configuration so the next crash writes a dump. The default dump file is the pagefile.&lt;/p&gt;

&lt;p&gt;On a physical server, check for Automatic Server Recovery (ASR) software, which restarts a machine it detects as unresponsive. On a Hyper-V virtual machine outside a cluster, the heartbeat feature restarts the VM when the host stops receiving its heartbeat. On VMware, check the VMware heartbeat feature and any third-party cluster software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related events and errors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Event 6008, logged at startup when the previous shutdown was unexpected.&lt;/li&gt;
&lt;li&gt;Event 1074, logged when an application or a user starts a shutdown or restart.&lt;/li&gt;
&lt;li&gt;Event 6006, a clean shutdown.&lt;/li&gt;
&lt;li&gt;Event 46 from volmgr, "Crash dump initialization failed!".&lt;/li&gt;
&lt;li&gt;Bug check 0x9F, DRIVER_POWER_STATE_FAILURE, the code in Microsoft's event 41 example.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its agent reads the kernel event stream on each Windows and Linux endpoint. ET Ducky has custom behavior detection by reading kernel logs and can be configured to look for things like this. Every alert gets an automatic root-cause analysis with the cause, impact, mitigation, prevention, and a confidence level. You can also open a Live Session shell from the browser without the need for a VPN and run the Get-WinEvent queries above on the machine that restarted.&lt;/p&gt;

&lt;p&gt;Alerts docs: &lt;a href="https://etducky.com/documentation/alerts" rel="noopener noreferrer"&gt;https://etducky.com/documentation/alerts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Microsoft Learn, Event ID 41 The system has rebooted without cleanly shutting down first; Advanced troubleshooting for stop code errors; Bug check code reference; Get-WinEvent; EventRecord.ToXml. All read 2026-10-28.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>troubleshooting</category>
      <category>powershell</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Finding What Keeps Locking Out an Active Directory Account</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Thu, 29 Oct 2026 01:21:25 +0000</pubDate>
      <link>https://dev.to/et-ducky/finding-what-keeps-locking-out-an-active-directory-account-1ehd</link>
      <guid>https://dev.to/et-ducky/finding-what-keeps-locking-out-an-active-directory-account-1ehd</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;An Active Directory account that locks out again minutes after you unlock it usually has a program, service, or saved credential still sending an old password. Windows records where each failed attempt came from, first on the domain controllers and then on the computer that sent it. This post covers how to follow that record from the lockout event to the process that holds the old password.&lt;/p&gt;

&lt;p&gt;A recent r/sysadmin thread (&lt;a href="https://www.reddit.com/r/sysadmin/comments/1xcgzhr/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/sysadmin/comments/1xcgzhr/&lt;/a&gt;) had a service account for a two-node high availability application that kept locking out. The domain controller logged Kerberos pre-authentication failures (event 4771) for it, and only one of the two nodes triggered them. The search then moves from the domain controller to that node, and the steps below find what on it sends the bad password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm the lockout on the PDC emulator
&lt;/h2&gt;

&lt;p&gt;Every domain controller forwards a failed password check to the domain controller that holds the PDC emulator role. The PDC emulator keeps the bad password count and locks the account, so I'd start there. Run this from a computer with the Active Directory PowerShell module, with &lt;code&gt;&amp;lt;account&amp;gt;&lt;/code&gt; replaced by the account name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pdc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-ADDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PDCEmulator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Get-ADUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pdc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Properties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LockedOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;BadPwdCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;LastBadPasswordAttempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;AccountLockoutTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;LockedOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;BadPwdCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;LastBadPasswordAttempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;AccountLockoutTime&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LastBadPasswordAttempt and AccountLockoutTime give you the times to match in every log that follows. When the account locks again within minutes of an unlock, the source is still running, so the events you need are being written while you look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the caller from the lockout event
&lt;/h2&gt;

&lt;p&gt;The PDC emulator writes event 4740 to its Security log when it locks the account. The Caller Computer Name field holds the name of the computer the failed attempt came from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ComputerName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pdc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Security'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4740&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,@{&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Account'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}},@{&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'CallerComputer'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Caller Computer Name is a workstation or a member server, go to that computer and skip to the section on finding the process. When it is blank or holds the name of a domain controller, the attempt likely came through a server that passed the credentials on. The authentication events on the domain controllers carry more detail in that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match the Kerberos and NTLM failures
&lt;/h2&gt;

&lt;p&gt;Each domain controller logs the failures it handled itself. Kerberos failures are event 4771, which is written only on domain controllers. Failure Code 0x18 on a 4771 means the wrong password was provided, and Client Address is the IP address of the computer that sent the request. NTLM failures are event 4776 with Error Code 0xC000006A, and its Source Workstation field holds the name of the computer the attempt came from.&lt;/p&gt;

&lt;p&gt;Event 4771 needs failure auditing for the Kerberos Authentication Service subcategory, and 4776 needs it for Credential Validation. Check both on one domain controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auditpol /get /subcategory:"Kerberos Authentication Service"
auditpol /get /subcategory:"Credential Validation"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then search every domain controller for both events from the last two hours and keep the ones for the account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$since&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Get-ADDomainController&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ComputerName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HostName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Security'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4771&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4776&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$since&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;account&amp;gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MachineName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the matching events in Event Viewer on each domain controller to read Client Address or Source Workstation. A Client Address that starts with ::ffff: is an IPv4 address, so resolve the part after it with Resolve-DnsName to get the computer name.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the domain controllers only point at another server
&lt;/h2&gt;

&lt;p&gt;When the source is a server that passes sign-ins on to the domain controllers, the Netlogon debug log on the PDC emulator records each forwarded attempt with the computer it came from. Turn it on from an elevated prompt on the PDC emulator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nltest /DBFlag:2080FFFF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let it run for 30 to 60 minutes while the lockouts continue. The log is %windir%\debug\netlogon.log, and it can't be read while Netlogon writes to it. Copy it to another folder and search the copy for 0xC000006A, the status for a bad password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="kd"&gt;netlogon&lt;/span&gt;.log &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;find&lt;/span&gt; &lt;span class="na"&gt;/i &lt;/span&gt;&lt;span class="s2"&gt;"0xC000006A"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;badpassword&lt;/span&gt;.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each matching line holds the account name and the name of the computer the attempt came from. Turn the debug log back off when you have the computer name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nltest /DBFlag:0x0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Find the process on the source computer
&lt;/h2&gt;

&lt;p&gt;The failed sign-in is also written on the computer where it was attempted, and that copy records which program made it. Turn on failure auditing for logons there from an elevated prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auditpol /set /subcategory:"Logon" /failure:enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the next lockout, find event 4625 in that computer's Security log at the same times as the failures on the domain controllers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Security'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4625&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4648&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-List&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sub Status 0xC000006A on a 4625 means the password was wrong. Caller Process Name holds the full path of the program that tried to sign in. Logon Type 4 is a batch sign-in such as a scheduled task, and Logon Type 5 is a service started by the Service Control Manager.&lt;/p&gt;

&lt;p&gt;When no 4625 matches, look at event 4648 at the same times. It records a sign-in with explicit credentials, and its Process Name field holds the program that supplied them. A Caller Process Name of lsass.exe with Logon Type 3 means the attempt came in over the network. Read Source Network Address and Workstation Name on that event and repeat this step on that computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check what still holds the old password
&lt;/h2&gt;

&lt;p&gt;On the computer where the record ends, list the services and scheduled tasks that run as the account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-CimInstance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Win32_Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StartName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*&amp;lt;account&amp;gt;*'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;StartName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;schtasks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/fo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Csv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Run As User'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*&amp;lt;account&amp;gt;*'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Run As User'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the stored password on each service and task, then restart the service. When the account belongs to a person, also check the credentials saved in their Windows profile with cmdkey /list while signed in as them, drives they mapped with saved credentials with Get-SmbMapping, and sessions they left signed in on other servers with quser /server:. When none of these match, the application likely makes the sign-in itself with a password saved in its own settings, so update the password there.&lt;/p&gt;

&lt;p&gt;Unlock the account after the last source is fixed, then check the PDC emulator for a new 4740 the next day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its Windows agent reads the kernel event stream through ETW on every managed computer, including process creation and the outbound connections each process opens. You can open a Live Session shell from the browser without the need for a VPN and run the auditpol and Get-WinEvent commands above on the node or domain controller you're tracing.&lt;/p&gt;

&lt;p&gt;You'd start a Multi-Agent Session across the node that triggers the lockouts and the node that doesn't. It compares the kernel events from both to find the outlier. Only counts-only summaries leave each host, and the raw kernel events stay on it.&lt;/p&gt;

&lt;p&gt;One Windows or Linux server on the Free query tier with your own AI key costs $5 a month (&lt;a href="https://etducky.com/pricing" rel="noopener noreferrer"&gt;https://etducky.com/pricing&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>windows</category>
      <category>activedirectory</category>
      <category>troubleshooting</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>0x800F0922 CBS_E_INSTALLERS_FAILED When Installing Windows Updates</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Sat, 24 Oct 2026 00:57:33 +0000</pubDate>
      <link>https://dev.to/et-ducky/0x800f0922-cbseinstallersfailed-when-installing-windows-updates-5a0m</link>
      <guid>https://dev.to/et-ducky/0x800f0922-cbseinstallersfailed-when-installing-windows-updates-5a0m</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;0x800F0922 is the Component-Based Servicing error CBS_E_INSTALLERS_FAILED. It appears when a Windows update fails to install, in the Windows Update history and in event 20 from Microsoft-Windows-WindowsUpdateClient in the System log. The event message reads "Installation Failure: Windows failed to install the following update with error" followed by the code and the update's name with its KB number. The same code appears when removing a Windows Server role or feature with Server Manager or Uninstall-WindowsFeature, with the message "CBS_E_INSTALLERS_FAILED Processing advanced installers and generic commands failed".&lt;/p&gt;

&lt;h2&gt;
  
  
  What it means
&lt;/h2&gt;

&lt;p&gt;An update package runs advanced installers for some of its components, such as registering a scheduled task. One of those installers failed, so Windows rolls the update back after all the operations in the installer queue finish. In Microsoft's example the failure is logged at startup and the update is then rolled back.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to find the cause
&lt;/h2&gt;

&lt;p&gt;Confirm which update failed and when, from an elevated PowerShell prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Microsoft-Windows-WindowsUpdateClient'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-List&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the installer that failed in CBS.log, at C:\Windows\Logs\CBS\CBS.log. Older entries roll over into CBSpersist files in the same folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Select-String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\Logs\CBS\CBS.log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Failed execution of queue item Installer'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SimpleMatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-First&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The line reads "Failed execution of queue item Installer" followed by the installer's name and the HRESULT it returned. A few lines later CBS logs "Failed to process advanced operation queue" with "HRESULT = 0x800f0922 - CBS_E_INSTALLERS_FAILED", then the rollback. Above the failure, a "Begin executing advanced installer phase" block lists the component being installed and the installer name.&lt;/p&gt;

&lt;p&gt;Run the same search on three or four affected machines. The same installer name and the same component on every machine points at something those machines share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixes
&lt;/h2&gt;

&lt;p&gt;When the installer is Task Scheduler and the component is Microsoft-OneCore-SecureBootEncodeUEFI-Task, the SecureBootEncodeUEFI scheduled task is corrupted. Microsoft's article on this case also shows event 146 in the Microsoft-Windows-TaskScheduler/Operational log, "Task Scheduler failed to load task \Microsoft\Windows\PI\SecureBootEncodeUEFI at service startup". Find and remove the staged update packages first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dism /english /online /get-packages /format:table | findstr /i "Staged"
Dism /online /remove-package /PackageName:&amp;lt;staged package name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then get the task's GUID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;reg&lt;/span&gt; &lt;span class="nb"&gt;query&lt;/span&gt; &lt;span class="s2"&gt;"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\Microsoft\Windows\PI\SecureBootEncodeUEFI"&lt;/span&gt; &lt;span class="na"&gt;/v &lt;/span&gt;&lt;span class="kd"&gt;ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete the task's registry entries with that GUID, then install the update again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;reg&lt;/span&gt; &lt;span class="kd"&gt;delete&lt;/span&gt; &lt;span class="s2"&gt;"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Maintenance\{GUID}"&lt;/span&gt; &lt;span class="na"&gt;/f
&lt;/span&gt;&lt;span class="nb"&gt;reg&lt;/span&gt; &lt;span class="kd"&gt;delete&lt;/span&gt; &lt;span class="s2"&gt;"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Plain\{GUID}"&lt;/span&gt; &lt;span class="na"&gt;/f
&lt;/span&gt;&lt;span class="nb"&gt;reg&lt;/span&gt; &lt;span class="kd"&gt;delete&lt;/span&gt; &lt;span class="s2"&gt;"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{GUID}"&lt;/span&gt; &lt;span class="na"&gt;/f
&lt;/span&gt;&lt;span class="nb"&gt;reg&lt;/span&gt; &lt;span class="kd"&gt;delete&lt;/span&gt; &lt;span class="s2"&gt;"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\Microsoft\Windows\PI\SecureBootEncodeUEFI"&lt;/span&gt; &lt;span class="na"&gt;/f
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installer is a different one, check what changed for that component on the affected machines, such as a service that was disabled or a scheduled task that was removed, and restore it before installing the update again.&lt;/p&gt;

&lt;p&gt;When the HRESULT after the installer name is one of the component store errors in Microsoft's article Fix Windows Update corruptions and installation failures, repair the component store and the system files, then retry the update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;DISM&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;/Online /Cleanup-Image /RestoreHealth
&lt;/span&gt;&lt;span class="nb"&gt;sfc&lt;/span&gt; &lt;span class="na"&gt;/scannow
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When 0x800F0922 appears while removing a Windows Server role or feature, check how many files C:\Windows\Temp holds. More than 65,000 files there can cause it. Delete the contents of C:\Windows\Temp, then remove the role or feature again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related events and errors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Event ID 20 from WindowsUpdateClient, the event that records every failed update install with its error code (&lt;a href="https://etducky.com/blog/windows-windowsupdateclient-event-id-20-installation-failure" rel="noopener noreferrer"&gt;https://etducky.com/blog/windows-windowsupdateclient-event-id-20-installation-failure&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Event 146 from TaskScheduler, a task that failed to load at service startup.&lt;/li&gt;
&lt;li&gt;0x80070002 (ERROR_FILE_NOT_FOUND), the HRESULT the Task Scheduler installer returns in the SecureBootEncodeUEFI case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its patch management talks to the Windows Update Agent directly and installs updates by KB number. You'd tag a few machines as your first ring and schedule updates to that tag before the rest. A patch is marked successful only when the kernel events show services back up with no crash loops. The deployment moves to the next ring only after the machines in the current ring pass that check, and a regression in a ring pauses the deployment. You can also open a Live Session shell from the browser without the need for a VPN and run the CBS.log search above on each machine in the ring.&lt;/p&gt;

&lt;p&gt;Patch management docs: &lt;a href="https://etducky.com/documentation/patch-management" rel="noopener noreferrer"&gt;https://etducky.com/documentation/patch-management&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Microsoft Learn, Error 0x800F0922 caused by corrupted scheduled task when installing Windows updates; Error 0x800f0922 when you uninstall roles; Fix Windows Update corruptions and installation failures; Troubleshoot Windows Update error 0x80070002; Event ID 20, WUA Update Installation; Get-WinEvent; Select-String. All read 2026-10-21.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>troubleshooting</category>
      <category>powershell</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Finding What Wakes Your Windows PC From Sleep</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Thu, 22 Oct 2026 14:59:04 +0000</pubDate>
      <link>https://dev.to/et-ducky/finding-what-wakes-your-windows-pc-from-sleep-2ek2</link>
      <guid>https://dev.to/et-ducky/finding-what-wakes-your-windows-pc-from-sleep-2ek2</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;A Windows PC that wakes up seconds after it goes to sleep, or turns itself on in the middle of the night, usually has a wake source that Windows records. This post covers how to ask Windows what woke it, how to check wake timers and scheduled tasks, and how to limit which devices are allowed to wake the PC.&lt;/p&gt;

&lt;p&gt;A recent ElevenForum thread had a PC that powered right back on after sleep because switching the trackball off with its slider moved it slightly. A mouse or trackball that moves while the PC is going to sleep can wake it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask Windows what woke it
&lt;/h2&gt;

&lt;p&gt;I'd run this in Terminal right after the PC wakes on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;powercfg /lastwake
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output lists the wake source from the last sleep. A device shows its name on the Friendly Name line, and a USB controller or a USB input device usually means a mouse, keyboard, or wireless receiver woke the PC. A wake timer shows as a timer with the process or service that set it.&lt;/p&gt;

&lt;p&gt;That command only covers the last wake. The System log keeps a record of every wake with the sleep time, the wake time, and the wake source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Microsoft-Windows-Power-Troubleshooter'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Match these entries to the times the PC turned itself on. Kernel-Power writes event 42 each time the PC starts to go to sleep, so list those entries for the same period and compare the two sets of times. A wake a few seconds after event 42 likely comes from input during sleep entry, such as the trackball in that thread. A wake at the same minute each night likely comes from a timer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'System'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ProviderName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Microsoft-Windows-Kernel-Power'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Wake Source line says Unknown, check the wake timers and scheduled tasks in the next section.&lt;/p&gt;

&lt;p&gt;On a PC that uses Modern Standby, &lt;code&gt;powercfg /a&lt;/code&gt; lists Standby (S0 Low Power Idle) as an available state. Run &lt;code&gt;powercfg /sleepstudy&lt;/code&gt; there as well. It writes an HTML report of the standby sessions from the last three days to the folder you ran the command from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check wake timers and scheduled tasks
&lt;/h2&gt;

&lt;p&gt;A PC that wakes at the same time every night is usually waking for a timer. List the timers that are set now from an elevated Terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;powercfg /waketimers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry names the process or task that set the timer and when it expires. Scheduled tasks can also wake the PC when their "Wake the computer to run this task" condition is ticked. This command lists every task with that setting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-ScheduledTask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WakeToRun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TaskPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;TaskName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Untick that condition on the Conditions tab of the task in Task Scheduler when the task doesn't need to wake the PC. To stop every timer from waking it, set Allow wake timers to Disable under Sleep in the advanced settings of the power plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limit which devices can wake the PC
&lt;/h2&gt;

&lt;p&gt;List every device that is allowed to wake the PC now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;powercfg /devicequery wake_armed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;powercfg /devicequery wake_programmable&lt;/code&gt; to see every device whose wake setting you can change, including the ones that are off now. Turn wake off for a device with its name exactly as the list shows it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;powercfg /devicedisablewake "HID-compliant mouse"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same setting is "Allow this device to wake the computer" on the Power Management tab of the device in Device Manager. Keep wake on for at least one device you use to wake the PC, or use the power button. For a network adapter, tick "Only allow a magic packet to wake the computer" on that tab to keep Wake-on-LAN working. Run &lt;code&gt;powercfg /deviceenablewake&lt;/code&gt; with the same name to turn wake back on for a device.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the PC hangs after a longer sleep
&lt;/h2&gt;

&lt;p&gt;In another ElevenForum thread a laptop woke fine from a short sleep but hung with the power light on after about 10 minutes of sleep. Windows can move a PC from sleep into hibernate after a set time, so check that timer first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;powercfg /q SCHEME_CURRENT SUB_SLEEP HIBERNATEIDLE
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Current AC Power Setting Index and Current DC Power Setting Index lines are in seconds written in hex, so 0x00000258 is 10 minutes. When one of them matches the time the hang starts, turn hibernate after sleep off and test a longer sleep.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;powercfg /change hibernate-timeout-ac 0
powercfg /change hibernate-timeout-dc 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the PC then wakes every time, the hang is likely in resuming from hibernate. Update the chipset and graphics drivers from the PC maker, or leave the timer off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its Windows agent reads the kernel event stream through ETW on every managed PC. You can open a Live Session shell from the browser without the need for a VPN and run the powercfg and Get-WinEvent commands above on a PC that keeps waking.&lt;/p&gt;

&lt;p&gt;You'd start a Live Session on the PC after it wakes on its own. The AI picks the ETW providers for each round of the investigation, and only counts-only summaries leave the PC while the raw kernel events stay on it. When several PCs in an office wake at night, a Multi-Agent Session across a PC that wakes and one that stays asleep compares the kernel events from both to find the outlier.&lt;/p&gt;

&lt;p&gt;One Windows or Linux server on the Free query tier with your own AI key costs $5 a month. Pricing is at &lt;a href="https://etducky.com/pricing" rel="noopener noreferrer"&gt;https://etducky.com/pricing&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>troubleshooting</category>
      <category>powershell</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Finding What Deleted Your Windows Firewall Rules</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Wed, 14 Oct 2026 14:52:33 +0000</pubDate>
      <link>https://dev.to/et-ducky/finding-what-deleted-your-windows-firewall-rules-586j</link>
      <guid>https://dev.to/et-ducky/finding-what-deleted-your-windows-firewall-rules-586j</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;Windows servers that suddenly block all traffic, and start working again when the firewall is turned off, often have a firewall with no rules left in it. The default inbound action blocks anything no rule allows, so an empty rule set looks like a network outage. This post covers how to confirm the rules are gone, how to find the program and account that deleted them, and how to restore them so the same change doesn't remove them again.&lt;/p&gt;

&lt;p&gt;A recent r/sysadmin thread had this across servers in several domains at once. The poster later wrote that new Microsoft Defender for Endpoint policies caused it, and reversing that change fixed every server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm the rules are gone
&lt;/h2&gt;

&lt;p&gt;Count the rules Windows is enforcing right now. The active store is the sum of the local rules, the Group Policy rules, and the Windows service rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-NetFirewallRule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PolicyStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ActiveStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Measure-Object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A count of zero, or a count far below a working server in the same role, confirms the rules were removed. Then check the default actions for each profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-NetFirewallProfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PolicyStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ActiveStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;DefaultInboundAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;DefaultOutboundAction&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run both commands on one working server as well, so you have a baseline to compare with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find what deleted them
&lt;/h2&gt;

&lt;p&gt;The firewall writes its own log under Applications and Services Logs &amp;gt; Microsoft &amp;gt; Windows &amp;gt; Windows Firewall With Advanced Security &amp;gt; Firewall in Event Viewer. Event 2033 reads "All rules have been deleted from the Windows Defender Firewall configuration on this computer." On Windows 11 the same message is event 2059. Event 2006 records a single rule being deleted, and event 2032 records the firewall being reset to its default configuration.&lt;/p&gt;

&lt;p&gt;List those events with the time they were written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Microsoft-Windows-Windows Firewall With Advanced Security/Firewall'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2006&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2033&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2059&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TimeCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-List&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these events has a Modifying User field and a Modifying Application field. Modifying Application holds the full path of the program that made the change, and Modifying User holds the account it ran as. A path to netsh.exe, powershell.exe, or an installer points at a script or a person. A path to svchost.exe running as SYSTEM is likely a Windows service applying policy, such as Group Policy or device management. Compare the time of the first event with the time the outage started.&lt;/p&gt;

&lt;p&gt;When several servers are affected, run the same query on each of them. The same Modifying Application at the same minute on every server points at a central change rather than something local to one machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find where the policy comes from
&lt;/h2&gt;

&lt;p&gt;Rules can come from the local store, from Group Policy, or from a device management service. List the source of every active rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-NetFirewallRule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PolicyStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ActiveStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TracePolicyStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Group-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PolicyStoreSourceType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it on the broken server and on a working one. When the working server has rules from GroupPolicy and the broken one has none, check the Group Policy result next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpresult /h C:&lt;span class="se"&gt;\T&lt;/span&gt;emp&lt;span class="se"&gt;\g&lt;/span&gt;p.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open C:\Temp\gp.html in a browser and find the Windows Defender Firewall with Advanced Security section. It names the GPO that sets the firewall policy. A setting that turns off rule merging for local rules means only the rules in that policy apply. When the servers are also managed by Intune or by Microsoft Defender for Endpoint security settings management, check the firewall policies assigned to them there, along with the date each policy was last changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn on the audit events before the next change
&lt;/h2&gt;

&lt;p&gt;The Security log can also record each rule that is added, modified, or deleted. Enable the audit subcategory on the affected servers, or set it in the GPO that applies to them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;auditpol /set /subcategory:&lt;span class="s2"&gt;"MPSSVC Rule-Level Policy Change"&lt;/span&gt; /success:enable /failure:enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Event 4946 is then written when a rule is added, 4947 when one is modified, and 4948 when one is deleted. Event 4954 records a change in the firewall Group Policy settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restore the rules
&lt;/h2&gt;

&lt;p&gt;Fix the source first. When the change came from a GPO, an Intune policy, or a Defender for Endpoint policy, reverse it there. A local repair is overwritten at the next policy refresh when the policy still deletes the rules.&lt;/p&gt;

&lt;p&gt;When the change was local, restore the default rule set from the server console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;netsh advfirewall reset
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reset removes every rule you added and returns Remote Desktop access to the Windows default, so run it from the console and not over RDP. To bring back your own rules, export the policy from a working server in the same role and import it on the broken one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;netsh advfirewall export "C:\Temp\working.wfw"
netsh advfirewall import "C:\Temp\working.wfw"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run gpupdate /force afterward, then count the active rules again and compare the count with the working server. Check the Firewall log again the next day in case the same program deletes the rules a second time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ET Ducky shows it
&lt;/h2&gt;

&lt;p&gt;I build ET Ducky, and its Windows agent reads process creation events from the kernel through ETW. The netsh or PowerShell process that ran in the minute before the rules disappeared shows up in that event stream with its parent and its command line. ET Ducky has custom behavior detection by reading kernel logs and can be configured to look for things like this, so a rule that matches a firewall reset or a delete-all command raises an alert when it runs.&lt;/p&gt;

&lt;p&gt;When several servers fail at once, you'd start a Multi-Agent Session across a broken server and a working one in the same role. It compares the kernel events from each host to find the shared cause and the outlier, and the raw events stay on the hosts. You can also open a Live Session shell from the browser without the need for a VPN and run the PowerShell commands above on each server.&lt;/p&gt;

&lt;p&gt;One Windows or Linux server on the Free query tier with your own AI key costs $5 a month. Pricing is at &lt;a href="https://etducky.com/pricing" rel="noopener noreferrer"&gt;https://etducky.com/pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Drafted with AI.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>sysadmin</category>
      <category>security</category>
      <category>powershell</category>
    </item>
    <item>
      <title>Ransomware Detection for Windows and Linux using ETW and eBPF</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Sat, 26 Sep 2026 17:24:31 +0000</pubDate>
      <link>https://dev.to/et-ducky/ransomware-detection-for-windows-and-linux-using-etw-and-ebpf-35m5</link>
      <guid>https://dev.to/et-ducky/ransomware-detection-for-windows-and-linux-using-etw-and-ebpf-35m5</guid>
      <description>&lt;p&gt;I build ET Ducky, an RMM that reads the kernel event stream on each endpoint. We launch on Product Hunt on Wednesday, September 30, and today I'm writing about the behavioral rule engine.&lt;/p&gt;

&lt;p&gt;Every managed agent runs thirteen built-in rules against its own kernel event stream. ETW supplies the events on Windows and eBPF tracepoints supply them on Linux. We funnel both into one event format, so each rule has one configurable definition that fires on either OS.&lt;/p&gt;

&lt;p&gt;The rules run on the same thread that delivers events, so a rule must never block. The engine is tuned to prefer false positives over false negatives. Per-process cooldowns suppress repeated firings of the same pattern, and the dashboard deduplicates duplicate firings.&lt;/p&gt;

&lt;p&gt;mass-file-rename fires at 25 or more files renamed to ransomware extensions within 60 seconds. file-encryption-sweep fires at 20 or more files given the same destination extension within 30 seconds, and a policy can change that window. mass-file-access fires at 200 or more distinct paths across 3 user-data root directories within 5 seconds. shadow-copy-deletion and ransom-note-pattern fire on a single match.&lt;/p&gt;

&lt;p&gt;The ransomware kill-chain rule is the only agent-side rule that reads the rule engine's output instead of raw events. It fires at Critical when two or more of shadow-copy-deletion, mass-file-rename, and ransom-note-pattern fire on the same process within five minutes. Its evidence list is the component detections, so the dashboard shows which base rules led to it without another database query.&lt;/p&gt;

&lt;p&gt;One definition has a limit on Linux. ETW gives both the old and the new file name for a rename. Our eBPF programs currently emit only the destination path for rename, renameat, and renameat2.&lt;/p&gt;

&lt;p&gt;ET Ducky is not an antivirus engine and does not replace one. The full rule set, thresholds, and evidence formats are in the original post at &lt;a href="https://etducky.com/blog/behavioral-security-monitoring-rules" rel="noopener noreferrer"&gt;https://etducky.com/blog/behavioral-security-monitoring-rules&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>etw</category>
      <category>architecture</category>
      <category>security</category>
      <category>linux</category>
    </item>
    <item>
      <title>What Polling, auditd, and eBPF Can See Through Linux RMM Agents</title>
      <dc:creator>Christopher Smith</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:41:33 +0000</pubDate>
      <link>https://dev.to/et-ducky/what-polling-auditd-and-ebpf-can-see-through-linux-rmm-agents-3k84</link>
      <guid>https://dev.to/et-ducky/what-polling-auditd-and-ebpf-can-see-through-linux-rmm-agents-3k84</guid>
      <description>&lt;p&gt;This post first appeared on the ET Ducky blog. I build ET Ducky, an RMM that reads ETW on Windows and eBPF on Linux.&lt;/p&gt;

&lt;p&gt;This is for anyone comparing RMM products for a fleet that includes Linux hosts. An RMM agent on Linux can be built three ways. It can poll /proc on a timer, it can subscribe to the kernel audit subsystem, or it can attach eBPF programs to kernel tracepoints. All three are described as Linux support. They answer different questions, and the difference shows up the first time you investigate something that already happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polling /proc on a timer
&lt;/h2&gt;

&lt;p&gt;The simplest agent reads /proc every fifteen or thirty seconds and reports what it finds. Load average, memory, disk usage, the process list at that instant, listening sockets. This is cheap, portable, and requires no privileges beyond the ability to read /proc.&lt;/p&gt;

&lt;p&gt;It is a sampling technique, so it sees what was true at the moment it looked. A process that starts, writes a file, opens a socket, and exits inside one polling interval leaves no trace at all. A build script that spawns four hundred short-lived children shows up as a CPU spike with no explanation of what produced it. When someone asks why the disk filled at 02:14, the answer available from polling is that it was not full at 02:00 and was full at 02:30.&lt;/p&gt;

&lt;p&gt;That is enough for capacity alerts and inventory. It is not enough for diagnosis, because the thing you need to look at has usually already exited.&lt;/p&gt;

&lt;h2&gt;
  
  
  The audit subsystem
&lt;/h2&gt;

&lt;p&gt;Linux has a kernel audit subsystem, configured with auditd and rules written against syscalls. Unlike polling it is event-driven, so a process that lives for eighty milliseconds still produces a record. The records carry the fields an investigation needs: the syscall, the arguments, the resolved path, the user and group, the process and parent.&lt;/p&gt;

&lt;p&gt;Two things make it awkward for an agent to own. The first is that auditd is a system-wide singleton. The rules are global, the log is global, and an agent that installs its own rules is editing configuration that the host's administrator, a compliance tool, or a security product may also be editing. Two products writing audit rules on the same host is a support conversation.&lt;/p&gt;

&lt;p&gt;The second is cost. Audit records are generated in kernel context and written out through a userspace daemon. A rule broad enough to be useful for diagnosis, such as auditing every openat, produces enough volume to be visible in benchmarks and to fill a disk on a busy host. The usual response is to narrow the rules until the volume is acceptable, which narrows what you can answer later.&lt;/p&gt;

&lt;h2&gt;
  
  
  eBPF programs on tracepoints
&lt;/h2&gt;

&lt;p&gt;eBPF programs run inside the kernel in a verified, sandboxed virtual machine. They attach to tracepoints, kprobes, and uprobes, filter and aggregate in kernel context, and emit only what the program decides to emit into a ring buffer the agent reads.&lt;/p&gt;

&lt;p&gt;The filtering location is the whole difference. An audit rule captures everything matching it and discards the excess in userspace, after paying the cost of generating and writing every record. An eBPF program decides in the kernel, so a rule like "process exec events, but rate-limited per process so a build loop cannot flood the buffer" costs nothing for the events it drops.&lt;/p&gt;

&lt;p&gt;The verifier is what makes this safe to ship to a customer fleet. It rejects programs that could fail to terminate, crash the kernel, or read memory outside permitted regions. A kernel module with the same access carries no such guarantee.&lt;/p&gt;

&lt;p&gt;The constraint is portability. eBPF programs read kernel structures, and those structures change between kernel versions. A program compiled against one kernel's layout reads the wrong offsets on another. CO-RE, compile once run everywhere, solves this by building against generic field accessors and relocating them at load time using the kernel's own BTF type data. Kernels that do not ship BTF need that type data supplied from elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions that separate the three
&lt;/h2&gt;

&lt;p&gt;These are answerable about any product, including this one, and the answers are specific rather than a yes or no on a feature grid.&lt;/p&gt;

&lt;p&gt;If a process starts and exits between two heartbeats, does the agent have a record of it? This separates polling from everything else.&lt;/p&gt;

&lt;p&gt;Does the agent install auditd rules? If so, what happens on a host where something else already manages them?&lt;/p&gt;

&lt;p&gt;What is the measured overhead under load, and what limits the agent's own resource use? A cgroup limit is a specific answer. "Lightweight" is not.&lt;/p&gt;

&lt;p&gt;What privileges does the agent hold at rest? CAP_BPF and CAP_PERFMON are narrower than root, and narrower than CAP_SYS_ADMIN.&lt;/p&gt;

&lt;p&gt;Which kernels are supported, and what happens on one that ships no BTF? Silent degradation and a clear error are both acceptable. Not knowing is not.&lt;/p&gt;

&lt;p&gt;Do Linux events reach the same queries and rules as Windows events, or is Linux a separate view with its own subset of features?&lt;/p&gt;

&lt;p&gt;That last question is the one most often skipped. An agent can capture excellent Linux telemetry and still leave you with two consoles, two rule languages, and a report that cannot span both halves of the fleet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ET Ducky answers them
&lt;/h2&gt;

&lt;p&gt;The Linux agent attaches eBPF programs to tracepoints in three provider groups, each of which can be switched off independently in AgentConfig.json: process exec and exit, file open and close, and network connect and accept over IPv4 and IPv6. Capture is rate-limited per process inside the kernel programs. No auditd rules are installed.&lt;/p&gt;

&lt;p&gt;Portability is CO-RE against the kernel's own BTF, which is standard on Ubuntu 22.04 and Fedora 36 onward. On RHEL 8, Ubuntu 18.04, and Amazon Linux 2, a matching .btf file dropped into /opt/etducky/agent/bpf/btfhub/ supplies the type data. Where neither is available the agent runs in a no-op capture mode: heartbeats, health metrics, live sessions, and remote desktop continue, and only kernel events are off. KernelEventsEnabled: false in the same config file turns capture off deliberately without uninstalling.&lt;/p&gt;

&lt;p&gt;The agent runs as an unprivileged etducky user under a hardened systemd unit. On kernel 5.8 and later it holds CAP_BPF and CAP_PERFMON rather than CAP_SYS_ADMIN; older kernels fall back to broader capabilities. Privileged actions go through operator-authorized elevation with an audit row per request. The unit applies cgroup limits of 512 MB of memory, 50% of a CPU, and 256 tasks.&lt;/p&gt;

&lt;p&gt;Events are normalized into the same shape the Windows agent produces and handed to the same downstream pipeline, so live-session analysis, inflection-bracketed correlation, and behavioral detections work identically on both platforms. A behavioral rule that detects a shell spawning a downloader runs on either operating system without being rewritten, and a cross-fleet query for top memory consumers returns rows from both.&lt;/p&gt;

&lt;p&gt;Two things the Linux agent does not do. It has no fine-grained user-mode provider taps equivalent to the Windows agent's user-mode ETW providers. It does not automatically isolate a host on encryption-sweep detection, which the Windows agent does. The cross-platform rule engine provides earlier-stage detection on Linux instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it on one host
&lt;/h2&gt;

&lt;p&gt;The quickest way to see the difference is to put one host under a kernel-event agent and ask it about something that finished before you looked. With a workspace and an enrollment token from the Agents page, the install is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://etducky.com/install.sh &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;sudo &lt;/span&gt;bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;TOKEN_ID&amp;gt; &lt;span class="nt"&gt;--subdomain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;SUBDOMAIN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run something on that host that exits in well under a second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'cat /etc/hostname &amp;gt; /tmp/etd-probe; rm /tmp/etd-probe'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a live session on the host and ask about recent process and file activity. The analysis is built from the kernel events, so a process that lived for a few milliseconds and a file that existed for less than that are both in it. Repeat the same command on a host under a polling agent and there is nothing to find. That is the whole difference, and it takes about ten minutes.&lt;/p&gt;

&lt;p&gt;The Linux agent guide covers the package options, the systemd unit, the sudoers policy, and the kernel requirements in full.&lt;/p&gt;

&lt;p&gt;The ten-minute test at the end works against any Linux agent. Start a sub-second process and check whether the agent recorded it.&lt;/p&gt;

&lt;p&gt;Drafted with AI, reviewed by me.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ebpf</category>
      <category>monitoring</category>
      <category>sysadmin</category>
    </item>
  </channel>
</rss>
