Skip to content

Process Operations

Ido Veltzman edited this page Jan 14, 2024 · 7 revisions

Process Protection

Protecting processes from killing and dumping using PsProcessType obcallback that runs every time a handle to a process is requested. This feature is not enabled when the driver is loaded reflectively.

Function Signature

OB_PREOP_CALLBACK_STATUS OnPreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)

RegistrationContext [PVOID]			    -- Unused.
Info		    [POB_PRE_OPERATION_INFORMATION] -- Contains important information such as process name, handle to the process, process type, etc.

Usage Example

# Protect process
NidhoggClient.exe process add <PID>

# Unprotect process
NidhoggClient.exe process remove <PID>

How It Works

The function begins by checking if the operation is a kernel handle operation. If it is, it returns OB_PREOP_SUCCESS.

Next, it checks if there are any protected processes. If there are none, it returns OB_PREOP_SUCCESS.

The function then gets the process ID of the process object in the Info structure.

It checks if the process ID is in the list of protected processes. If it is, it removes the following permissions from the process:

  • PROCESS_VM_OPERATION: The ability to perform virtual memory operations.
  • PROCESS_VM_READ: The ability to read from the process's virtual memory.
  • PROCESS_CREATE_THREAD: The ability to create a new thread in the process.
  • PROCESS_DUP_HANDLE: The ability to duplicate the process's handles.
  • PROCESS_TERMINATE: The ability to terminate the process. Finally, it returns OB_PREOP_SUCCESS.

Process Hiding

Hiding processes by removing the entry from from the process links list. This operation is not PatchGuard safe.

Function Signature

NTSTATUS ProcessUtils::HideProcess(ULONG pid)

pid [ULONG] -- PID to hide.

Usage Example

# Hiding process
NidhoggClient.exe process hide <PID>

# Unhiding process
NidhoggClient.exe process unhide <PID>

How It Works

The function begins by getting the offsets of the ActiveProcessLinks and ProcessLock in the EPROCESS structure. If either of these operations fail, it returns STATUS_UNSUCCESSFUL.

Next, it looks up the process by the provided PID. If this operation fails, it returns the status of the operation.

The function then gets the list entry of the process in the process list and the lock of the process list.

It acquires the process list lock to avoid accessing problems.

The function then adds the process to the list of hidden processes. If this operation fails, it releases the process list lock, dereferences the process object, and returns STATUS_UNSUCCESSFUL.

The function then removes the process from the process list, releases the process list lock, and dereferences the process object.

Finally, it returns the status of the operation. If the operation was successful, it means that the process was successfully hidden. If the status is STATUS_UNSUCCESSFUL, it means that the process was not hidden.


Process Elevation

Elevating process by changing the token to SYSTEM token.

Function Signature

NTSTATUS ProcessUtils::ElevateProcess(ULONG pid)

pid [ULONG] -- PID to elevate.

Usage Example

NidhoggClient.exe process elevate<PID>

How It Works

The function begins by looking up the target process using the provided PID and the SYSTEM process. It also gets the offset of the token in the EPROCESS structure.

If either of these operations fail, it returns the status of the operation.

The function then replaces the token of the target process with the token of the SYSTEM process. This effectively gives the target process the same privileges as the SYSTEM process.

Finally, it dereferences the SYSTEM process and the target process and returns the status of the operation. If the operation was successful, it means that the process was successfully elevated. If the status is not STATUS_SUCCESS, it means that the process was not elevated.


Process Signature Modification

Modifying the process signature to give or strip any kind of PP (Protected Process) or PPL (Protected Process Light).

Function Signature

NTSTATUS ProcessUtils::SetProcessSignature(ProcessSignature* ProcessSignature)

ProcessSignature [ProcessSignature*] -- Contains the process PID, signer type and signature signer.

Usage Example

NidhoggClient.exe process signature <PID> <SIGNER TYPE> <SIGNATURE SIGNER>

How It Works

The function begins by looking up the process using the PID from the ProcessSignature structure. If this operation fails, it returns the status of the operation.

Next, it calculates the new signature level by shifting the signer type left by 4 bits and ORing it with the signature signer.

The function then gets the process signature by adding the signature level offset to the process.

It sets the SignatureLevel, Type, and Signer of the process signature to the new signature level, signer type, and signature signer, respectively.

Finally, it dereferences the process and returns the status of the operation. If the operation was successful, it means that the process signature was successfully set. If the status is not STATUS_SUCCESS, it means that the process signature was not set.


Thread Protection

Protecting threads from killing using PsThreadType obcallback that runs every time a handle to a process is requested. This feature is not enabled when the driver is loaded reflectively.

Function Signature

OB_PREOP_CALLBACK_STATUS OnPreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)

RegistrationContext [PVOID]			    -- Unused.
Info		    [POB_PRE_OPERATION_INFORMATION] -- Contains important information such as thread name, handle to the thread, thread type, etc.

Usage Example

# Protect thread
NidhoggClient.exe thread add <TID>

# Unprotect thread
NidhoggClient.exe thread remove <TID>

How It Works

The function begins by checking if the operation is a kernel handle operation. If it is, it returns OB_PREOP_SUCCESS.

Next, it checks if there are any protected threads. If there are none, it returns OB_PREOP_SUCCESS.

The function then gets the thread ID and the owner process ID of the thread object in the Info structure. It also gets the caller process ID.

It checks if the caller process is the owner of the thread or the SYSTEM process. If it is, it returns OB_PREOP_SUCCESS.

The function then checks if the thread ID is in the list of protected threads. If it is, it removes the following permissions from the thread:

  • THREAD_TERMINATE: The ability to terminate the thread.
  • THREAD_SUSPEND_RESUME: The ability to suspend and resume the thread.
  • THREAD_SET_CONTEXT: The ability to set the context of the thread. Finally, it returns OB_PREOP_SUCCESS.

Thread Hiding

Hiding a thread by removing it from the thread list entry. Upon Nidhogg unloading, the thread is restored to the original process or explorer if the original process died to avoid BSODs.

Function Signature

NTSTATUS ProcessUtils::HideThread(ULONG tid)

tid [ULONG] -- TID to hide.

Usage Example

# Hide thread
NidhoggClient.exe thread hide <TID>

# Unhide thread
NidhoggClient.exe thread unhide <TID>

How It Works

The function begins by getting the offsets of the ThreadListEntry and ThreadLock in the ETHREAD structure. If either of these operations fail, it returns STATUS_UNSUCCESSFUL.

Next, it looks up the thread by the provided TID. If this operation fails, it returns the status of the operation.

The function then gets the owning process of the thread. If the owning process is NULL, it dereferences the thread object and returns STATUS_NOT_FOUND.

It gets the PID of the owning process. If the PID is 0, it dereferences the thread object and returns STATUS_NOT_FOUND.

The function then gets the list entry of the thread in the thread list and the lock of the thread list.

It acquires the thread list lock to avoid accessing problems.

The function then tries to remove the thread from the thread list. If this operation fails, it sets the status to STATUS_UNSUCCESSFUL.

It releases the thread list lock and dereferences the thread object.

If the status of the operation is successful, it adds the thread to the list of hidden threads.

Finally, it returns the status of the operation. If the operation was successful, it means that the thread was successfully hidden. If the status is STATUS_UNSUCCESSFUL, it means that the thread was not hidden.