DoppelPaymer targets both antivirus (AV) and endpoint detection and response (EDR) applications with Process Hacker, exploiting KProcessHacker. A blog post published in July 2019 by Crowd strike Intelligence stated that Process Hacker had been hijacked to take out a list of targeted processes to deliver a “critical hit”. Although the blog is now a couple of years old, the hijacking technique is interesting enough to dig into its implementation. The malicious stager DLL used by Process Hacker exploits legitimate behaviors. A theft-proof process, including Protected Process Light (PPL)-protected processes, will be terminated by the Stagger DLL. This task is accomplished through Process Hacker’s kernel driver, KProcessHacker, which has been registered under the service name KProcessHacker3. ProcessHacker hijacks legitimate activity by loading a malicious stager DLL. ProcessHacker is launched by DoppelPaymer by placing the ProcessHacker executable, the KProcessHacker driver, and the malicious stager DLL into a subdirectory of %APPDATA%. A unique string of alphanumeric characters is used in the name of the subdirectory and the file names of the executable and driver. Following the writing of those two files, one of the DLLs loaded by ProcessHacker is hijacked by using a technique called “DLL search order hijacking”.
Hijacking the DLL Search Order
DoppelPaymer, like Dridex, exploits DLL search order hijacking to take advantage of Windows processes’ DLL loading behaviour. The operating system PE loader must load both the binary and the DLL files required for the PE to function. When seeking for DLL files to load, MS Windows uses a certain route by default. Before checking the Windows system directories, Windows looks for Windows system DLLs in the same directory as the target programme. In this situation, DoppelPaymer, a malicious process, can drop a malicious version of a DLL in that directory, which will be loaded by the target application.
DoppelPaymer searches the module name list in the ProcessHacker binary’s Import Address Table (IAT) to decide which DLL to hijack. Each name is CRC32 hashed and compared to a hardcoded list of hashes (Table 1), and if a match is found, the name is added to a list data structure. One of the three names on the list is chosen using a random number generator.
The authentic Windows version of a DLL is read into a memory buffer once a DLL has been selected. The malicious stager DLL is built from this DLL as a template. The hijacked DLL is written to the same folder as the ProcessHacker programme.
Developing the Procedure
The first is the name of the KProcessHacker.sys driver, and the second is an integer that will be used for inter-process communication (IPC) between the DoppelPaymer and ProcessHacker processes.
C:\Users\ducksoup\AppData\Roaming\M28fPT\ibOLR 2LEQV0 161604546
Configuring the IPC Objects
To communicate between the two processes, event handlers and section objects are employed. DoppelPaymer may connect directly with the stager DLL that is loaded inside the ProcessHacker process using these items. When referring these objects, the example handle values in Table 2 are utilised throughout the rest of this post. These numbers change depending on how DoppelPaymer is used.
|Object Type||Handle Value||Purpose|
|Event Object||0x120||Notify data in queue|
|Event Object||0x11C||Notify data processed|
|Section Object||0x124||Queue used to send process information to the stager DLL|
|Section Object||0x128||Contains the three events|
Table 2 shows how IPC deals with concrete test results.
DoppelPaymer can write data to the objects because a view is mapped into process memory for each section object. The 0x124 object represents the queue where the process information for the processes that are about to terminate will be written. The handle values of the other three objects, 0x120, 0x11C, and 0x124, will be stored in the 0x128, the other object. DoppelPaymer must provide ProcessHacker with the 0x128 handle value so that the stager DLL can access those three handles.
In the sample command line shown in Figure 1, the second parameter to ProcessHacker is the section object handle 0x128 XORd against the same constant value (unique per binary) utilised throughout DoppelPaymer’s lifetime. The constant for this binary is 0x9a1e2ea. The decimal value 161604546 is obtained by XORing 0x128 with 0x9a1e2ea.
CreateProcessW is called to launch ProcessHacker when these IPC objects have been created and the second parameter to ProcessHacker has been generated. Before establishing inter-process communication, DoppelPaymer must wait for the stager DLL to initialise inside the ProcessHacker process. For event handle 0x120, NtWaitForSingleObject is called, and DoppelPaymer waits for that event to be signalled.
The Stager DLL is being loaded.
In ProcessHacker, the stager DLL is loaded. Before the stager DLL can use KProcessHacker to destroy processes, it must go through several startup steps:
The entry point for ProcessHacker must be changed to ensure that none of ProcessHacker’s startup procedures run.
It’s necessary to start the KProcessHacker service.
The stager DLL and ProcessHacker must be confirmed as valid clients for the KProcessHacker service.
DoppelPaymer requires double IPC objects in order to communicate with the stager DLL.
The stager DLL can begin killing target processes provided by DoppelPaymer once all four of these phases have been successfully performed.
Process of Getting There Address of a Hacker’s Code Entrypoint
The malicious code will begin to execute once the process begins to load the stager DLL, but it will not be used by DoppelPaymer unless control is transferred back to the OS to finish loading ProcessHacker. When ProcessHacker’s entry-point address is reached, the loading procedure is complete. The stager DLL will overwrite ProcessHacker’s entry point with the code in Figure 3 to determine when the entry point is reached.
.rdata:10006120 mov eax, 94A351BBh
.rdata:10006125 push 0
.rdata:10006127 push 8FF4B5ACh ; Event handle
.rdata:1000612C call eax ; NtSetEvent
.rdata:1000612E push 0
.rdata:10006130 push 1
.rdata:10006132 push 0FFFFFFFEh
.rdata:10006134 mov eax, 1DCB264Eh
.rdata:10006139 call eax ; NtWaitForSingleObject
.rdata:1000613B jmp short loc_1000612E
This code is taken from the stager DLL’s.rdata section and updated to reflect the current process environment. The event handle, as well as the two Windows API calls utilised in the notification routines, have placeholders. The event that indicates the entry point has been reached is created and copied to the placeholder 8FF4B5ACh. NtSetHandle and NtWaitForSingleObject addresses are resolved and written to the locations 94A351BBh and 1DCB264Eh, respectively.
This thread can continue and initialise the KProcessHacker driver once the “entry point reached” event is notified. The stager DLL must register the driver and construct the KProcessHacker service. This task is accomplished using essentially the same code as the two ProcessHacker routines contained in the kph.c source code:
KProcessHacker Client Kernel Verification
When an IOCTL is delivered to the KProcessHacker service, it is confirmed that the caller is a validated KProcessHacker client who is authorised to communicate with the service. The IOCTL request key generated by submitting a KPH RETRIEVEKEY request from the user-mode process is used to validate any attempts to communicate with KProcessHacker. The importance of this key is detailed in the section below under “KProcessHacker IOCTL Request Keys and APC.” KphpWithKeyApcRoutine, an Asynchronous Procedure Call (APC) routine, is attached to the KPH RETRIEVEKEY request and will be run upon completion.
Getting Rid of Blocklisted Apps
DoppelPaymer begins enumerating both service and process names and hashing them with the CRC32 algorithm once it receives the signalled event. In DoppelPaymer’s process memory, these hashes are compared against a list of blocklisted hashes. The entire list was discussed in detail in a previous DoppelPaymer blog post. This section explains what happens if an application matches one of the items in the blocklist.
IOCTL Request Keys and APC for KProcessHacker
The verification of an IOCTL request key is required for some IOCTL requests to the KProcessHacker service. The key is produced by the driver and placed in the KPH CLIENT structure to ensure that it cannot be tampered with. A key is required for the following IOCTL requests:
Assassinating a Process
DoppelPaymer checks to see if the process was successfully started before proceeding to the next step. If an error occurs, it checks for blocklisted apps again; otherwise, another notice is delivered, this time with the command 2 to end the procedure.
Terminating a process is similar to starting one, with the exception that the StagerKillProcess function pointer is supplied to the StagerAPCRoutine. The KProcessHacker service receives a KPH TERMINATEPROCESS request from the StagerKillProcess function. The kernel-mode function KpiTerminateProcess takes care of this. Before the process may be terminated, the request key must be validated. The target process is reopened in order to obtain a kernel handle, and ZwTerminateProcess is used to terminate it. This technique does not take into account PPL, therefore even protected processes will be destroyed.
DoppelPaymer’s use of ProcessHacker to disable antivirus services is part of a bigger trend in which diverse actors use legitimate technologies to disable antivirus and anti-virus software.