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
wmainfunction is a dispatcher. Based on the first command-line argument, it calls the corresponding “main” function. - All the arguments are with a
-except/hcand/h. - Both
/hcand/hdo 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:
-srefers to a shared memory handle.-prefers to the process id of the crashing process.-iprefers to a initiating process id.
The relevant steps performed by this function:
- Parse and validate command-line arguments.
-sis mandatory and-pare mandatory.-ipis optional. - Open a handle to the process with pid
-p. - If
-ipis set, open a handle to the process with pid-ip. - Else open a new handle to the process with pid
-pand use it as if it’s the initiation process id. - Call
WerpInitiateCrashReporting— as seen in screenshot below.

A few things to note in this screenshot:
XerDumpNotifyStartandXerDumpNotifyCompleteare both imported fromext-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.WerpInitiateCrashReportingis imported fromfaultrep.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:
- Initialize a
CProcessInformationobject containing the process id of the crashing process. - Initialize a
CHangReportobject. - Initialize a
CUserModeHangReportobject. - Create the actual report by calling
CHangReport::Create(). - 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.
-ipthe initiating process id.-pthe process id.-sa 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.
- Validate input.
- Open a handle to specified process (
-p). - Open a handle to specified initiating process(
-ip). If there is none, open a handle to same process as specified with-p. - 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.
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:
-iThe 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.-eThe exiting process.-tThe thread id that sends the SilentProcessExit signal to WerFault. This is a thread id inside the process referenced by-i.-cThe statuscode passed to ExitThread/TerminateThread.
The table describing the registry entries “accidentily” documents this as well.
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
[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:...
I’m in your logs now: deceiving analysts and blinding EDRs
[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:...
FalconFriday: Need for Speed: going underground with near-real-time (NRT) rules – 0xFF26
[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



