Dissecting WerFault
November 6, 2026

Henri Hambartsumyan

This blog post documents the command-line parameters of WerFault.exe, obtained by reverse engineering the binary. Some inner WerFault workings are also discussed.

A side note: I started this small side project in 2023. Reversing was, back then, still mostly a manual effort. Current AI tools can do this in a few minutes with probably more accurate descriptions, but I hope you can enjoy some artisanal reverse engineering.

Introduction

Modern Windows versions come with a large number of executables files. My system32 folder alone currently has 879 .exe files, while the wider C:\Windows has 5748 .exe files. However, only a small selection of those binaries have properties which are special and worth investigating. 

One of those binaries is WerFault.exe and its significant other WerFaultSecure.exe. There are two main reasons why WerFault is interesting from security perspective:

  • It’s designed to create memory dumps and snapshots of other processes.
  • There is limited documentation on the inner workings and command-line arguments.

The first reason is interesting from an offensive perspective. Attackers can and do leverage WerFault. For example, to dump LSASS with “silentprocessexit”. But are there any other attack paths to leverage for offensive purposes?

The second reason — limited documentation — is very annoying for detection engineering, incident triage and incident response purposes. I found a blog post from Helge Klein, which documents some things. Also, Hexacorn has a blog post on this topic, but in there are still unknowns which intrigue me. The best documentation I found so far was (of course 😉) the Windows Internals book. 

In this blog post, I’ll combine the information from the aforementioned three sources with knowledge obtained by reversing WerFault. 

Dispatching the call

A logical place to start understanding the command-line argument is to start with the wmain function. Loading werfault.exe into IDA and loading the Microsoft-provided symbols, we can hit the ground running.

Very quickly inside wmain you can see the parsing of the command-line arguments.

 

 Parsing the command-line arguments in wmain.

And thanks to the symbols provided by Microsoft, we can already make out that the the first command-line option determines the “mode” of WerFault.

Here, we notice a few things:

  • The wmain function is a dispatcher. Based on the first command-line argument, it calls the corresponding “main” function.
  • All the arguments are with a - except /hc and /h.
  • Both /hc and /h do the same. 

All other command-line arguments are dependent on the specific function. The specific features, as suggested by the functions names, are also confirmed by the two blog posts mentioned earlier and the Windows Internals book. So far, so good.

In the next sections, we’ll explore each of these functionalities individually.

UserCrashMainSEH

Based on the name, we can fairly assume that this function is invoked whenever a normal “user process” crashes. SEH in this context likely points to Structured Exception Handling.

Looking again, the code is fairly easy to read in disassembled form, as well as in assembly. 

Here, we’re seeing again the parsing of the command-line arguments passed to UserCrashMain. Based on the error messages we can drive:

  • -s refers to a shared memory handle.
  • -p refers to the process id of the crashing process.
  • -ip refers to a initiating process id.

 The relevant steps performed by this function:

  1. Parse and validate command-line arguments. -s is mandatory and -p are mandatory. -ip is optional.
  2. Open a handle to the process with pid -p.
  3. If -ipis set, open a handle to the process with pid -ip.
  4. Else open a new handle to the process with pid -p and use it as if it’s the initiation process id. 
  5. Call WerpInitiateCrashReporting — as seen in screenshot below.

A few things to note in this screenshot:

  • XerDumpNotifyStart and XerDumpNotifyComplete are both imported from ext-ms-win-wer-xbox-l1-1-1.dll. This is a weird library I can’t find on any system I have access to. I’m also unsure what the relationship is with xbox. If you have any clue or have a copy of this binary, please let me know. Google only has 3 hits on this filename. There is nothing on VT.
  • WerpInitiateCrashReporting is imported from faultrep.dll.

There is plenty of things going on inside WerpInitiateCrashReporting, but we’ll skip that for now as it’s not relevant for the scope of this blog post.

ReflectionServerMain

This next function name doesn’t reveal much (to me) about what it does. Looking at the code, we see that this function only accepts one command-line argument with its corresponding value, being -pr XXXX. If the number of parameters provided is incorrect, it returns error code 0x80070057, which translates to “The parameter is incorrect”.

In the happy flow, we’re going to start the ReflectedDumpServer, passing in the value of the -pr argument — whatever that means.

The value of the -pr is “The name of the file mapping object to be opened.” It’s passed to the lpName parameter of OpenFileMappingW. Based on the description of the workflow in the Windows Internals book Chapter 10, section “Windows Error Reporting”, I suspect the handle name passed to WerFault is the name of the shared memory object, potentially created by the crashing process. More debugging and reversing is required to confirm this. 

While following the decompiled code in IDA, it seems like the main goal of this mode ends up in a call to RtlCreateProcessReflection. This seems to be an undocumented function. However, there are plenty of details documented by all kinds of security researchers. Moreover, some details are described in “Windows Internals, 6th edition, Part 2”. The latest edition (7th) doesn’t include this section. It’s a function which allows a process to be cloned. The function creates a minidump with MiniDumpWriteDump , clones the process and “resumes” it. I suspect this is an attempt to restore a crashing process. But again, it requires more reversing to properly understand this.

This is potentially a vector to either create a memory dump of LSASS directly or to clone LSASS with RtlCreateProcessReflection and then dump it. But this full attack chain is still unproven.

WerCoreReportingHang

Before diving into how this function works, note that in multiple ways this functionality feels like it’s been bolted on “after the fact”. First, there is the obvious difference in expecting command-line arguments with a / instead of a -. Next is the fact that it accepts two different command-line switches for the same function. All others have exactly one switch. Finally, the way the function is invoked is different. Consider the next screenshot from IDA.

All functions take the full argc/argv pair, as passed to wmain , while a call to WerCoreReportHang strips the first command-line argument from argc/argv. 

The only command-line arguments which seems to be actually parsed in this function is the /SHARED argument. While based on actual process creation data in various environments, the /t and /p arguments seem to be passed. Either way, /p most likely refers to the process id again, while /t might refer to a thread id in case of a thread hang, but I can’t confirm this based on the code.

As the function name suggest, this function is meant for error reporting on processes that are hanging. The first step is to check if the system has been configured to attach a debugger, based on HKLM\Software\Microsoft\Windows\Windows Error Reporting\Hangs\Debugger. If a debugger is set, it executes the debugger. If applicable, it then waits until a debugger is attached and breaks. 

Next, it resets the PATH environment variable to the System32 folder, and sets a bunch more environment variables starting with _NT_. 

Then, it checks if a /SHARED argument is provided, which points to a shared memory object, just like earlier. If so, this shared memory is opened, copied locally and the structure is validated to see if it matches the undocumented type SharedHangRepData struct. 

We’ll skip most of the details of the next steps, but roughly the following happens:

  1. Initialize a CProcessInformation object containing the process id of the crashing process.
  2. Initialize a CHangReport object.
  3. Initialize a CUserModeHangReport object.
  4. Create the actual report by calling CHangReport::Create().
  5. Submit the error report by calling CHangReport::Submit().

Obviously, this is an oversimplification, since a lot is happening , but the details are not very relevant for this blog post. 

SnapshotCaptureMain

This mode, triggered by -pss, requires the same arguments as UserCrashMainSEH.

  • -ip the initiating process id.
  • -p the process id.
  • -s a handle to a shared memory object.

Note that here we’re dealing with a handle to the shared memory, opposed to a global name.

The flow for this function is straightforward.

  1. Validate input.
  2. Open a handle to specified process (-p).
  3. Open a handle to specified initiating process(-ip). If there is none, open a handle to same process as specified with -p.
  4. Make a snapshot of that process.

The last step — creating a snapshot — is actually an interesting procedure, as it might offer some opportunities for LSASS dumping again. This does require some tinkering, as WerFault performs certain checks on the validity of the data in the shared memory section. Also it validates some other settings. For example, if the taking snapshots is allowed on the current system.

The one thing which is still unclear to me, is how WerFault is supposed to MapViewOfFile , where the handle is directly a command-line argument and thus — by definition — external to WerFault. This is almost certainly related to shared memory sections, and the error reporting port which all Windows processes seem to have.

SilentProcessExitMain

This function is implemented as a proxy to the function WerUIReportSilentProcessExit in werui.dll. See the screenshow below.

The implementation of SilentProcessExitMain in werfault.exe.

Jumping into the implementation of this function in werui.dll, we see more weird things happening. It parses argc/argv looking for the following command-line arguments:

  • -i The initiating process id. If the process is self-terminating, this is the same as the exiting process. If the silent process termination is triggered cross-process, this is the id of the process that started the termination.
  • -e The exiting process. 
  • -t The thread id that sends the SilentProcessExit signal to WerFault. This is a thread id inside the process referenced by -i.
  • -c The statuscode passed to ExitThread/TerminateThread.

The table describing the registry entries “accidentily” documents this as well. 

Source: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/registry-entries-for-silent-process-exit#monitor-process

Since the details of how “Silent Process Exit” works are described in a lot of detail and the reversing didn’t reveal anything new, we won’t go into any more depth on this. 

KernelCrashMain

KernelCrashMain handles both crash dumps and live kernel reports, so the name is a little misleading. It first parses the argument after -k to select an operation for this mode.

The command-line options which determine the operation are:

  • -c “Convert dump and report”. This operation processes an existing crash dump, queues the resulting kernel report and then attempts to report from the kernel queue.
  • -q “Report from KQueue”. This operation skips the dump processing and starts working on reporting from the kernel queue directly.
  • -l “Report live report”. Find and queue one live report into the KQueue.
  • -lc “Convert live dump and report”. Process a full live dump and then submit it to WER.
  • -lcq “Report all live reports”. This traverses the KQueue and submits each item to WER.

Final remarks

WerFault.exe is more than just a crash reporting utility. Depending on its command-line arguments, it can perform a variety of operations, including creating process snapshots, cloning processes, and generating memory dumps.

Hopefully, this breakdown helps demystify some of WerFault’s less-documented behavior and provides a useful reference for analysts trying to distinguish legitimate activity from potential abuse.

Knowledge center

Other articles

Introducing FalconDash, your tuning companion

Introducing FalconDash, your tuning companion

[dsm_breadcrumbs show_home_icon="off" separator_icon="K||divi||400" admin_label="Supreme Breadcrumbs" _builder_version="4.18.0" _module_preset="default" items_font="||||||||" items_text_color="rgba(255,255,255,0.6)" custom_css_main_element="color:...

FalconForce realizes ambitions by working closely with its customers in a methodical manner, improving their security in the digital domain.

Energieweg 3
3542 DZ Utrecht
The Netherlands

FalconForce B.V.
[email protected]
(+31) 85 044 93 34

KVK 76682307
BTW NL860745314B01

ISO27001 certified