-
Notifications
You must be signed in to change notification settings - Fork 281
Memory Operations
Patching a function inside a usermode module, can either use built in patches for AMSI and ETW or your own.
NTSTATUS MemoryUtils::PatchModule(PatchedModule* ModuleInformation)
ModuleInformation [PatchedModule*] -- Pointer to a PatchedModule structure that contains information about the module to be patched.
# Patching AMSI
NidhoggClient.exe patch <PID> amsi
# Patching ETW
NidhoggClient.exe patch <PID> etw
# Patch arbitrary module
NidhoggClient.exe patch <PID> <Module Name> <Function> <Patch comma separated>
The function begins by copying the module name and function name from the ModuleInformation
structure to local variables.
Next, it looks up the target process using the process ID from the ModuleInformation
structure.
The function then attaches to the target process and retrieves the base address of the module to be patched.
If the module base address is successfully retrieved, the function gets the address of the function to be patched within the module.
Finally, the function patches the module by writing the patch data from the ModuleInformation
structure to the function address in the target process, and then detaches from the target process.
If any of the steps fail, the function returns an error status. Otherwise, it returns the status of the patch operation.
Hide usermode module from PEB of a process and from the VAD tree.
NTSTATUS MemoryUtils::HideModule(HiddenModuleInformation* ModuleInformation)
ModuleInformation [HiddenModuleInformation*] -- Pointer to a HiddenModuleInformation structure that contains the process ID and the name of the module to be hidden.
NidhoggClient.exe module hide <PID> <PATH>
The function begins by allocating memory for the module name and copying the module name from the ModuleInformation
structure.
Next, it looks up the target process using the process ID from the ModuleInformation
structure.
The function then attaches to the target process and retrieves the PEB
of the target process.
If the PEB
is not found, the function detaches from the target process, dereferences the target process, and returns STATUS_ABANDONED
.
The function then waits for the loader data in the PEB
to be available. If the loader data is not available after 10 attempts, the function detaches from the target process, dereferences the target process, and returns STATUS_ABANDONED_WAIT_0
.
The function then iterates over the InLoadOrderModuleList
in the loader data. For each module in the list, it checks if the module name matches the module name to be hidden. If a match is found, it removes the module from the InLoadOrderModuleList
, InInitializationOrderLinks
, InMemoryOrderLinks
, and HashLinks
, and sets the status to STATUS_SUCCESS
.
The function then detaches from the target process.
If the module was successfully found and removed, the function hides the module in the VAD
tree of the target process.
Finally, the function dereferences the target process and returns the status of the operation.
Injecting a shellcode to usermode process via either APC or remote thread.
NTSTATUS MemoryUtils::InjectShellcodeAPC(ShellcodeInformation* ShellcodeInfo)
ShellcodeInfo [ShellcodeInformation*] -- Pointer to a ShellcodeInformation structure that contains all the information regarding the injected shellcode.
NTSTATUS MemoryUtils::InjectShellcodeThread(ShellcodeInformation* ShellcodeInfo)
ShellcodeInfo [ShellcodeInformation*] -- Pointer to a ShellcodeInformation structure that contains all the information regarding the injected shellcode.
# Inject shellcode with remote thread
NidhoggClient.exe shinject thread <PID> <PATH>
# Inject shellcode with APC
NidhoggClient.exe shinject apc <PID> <PATH>
The function begins by looking up the target process using the process ID from the ShellcodeInfo
structure.
Next, it finds an APC suitable thread (alertable thread, non GUI) in the target process.
The function then opens the target process and allocates memory for the shellcode.
If the memory is successfully allocated, the function writes the shellcode to the allocated memory in the target process.
The function then creates two APCs
: a preparation APC and the shellcode APC. The preparation APC is used to prepare the target thread for the shellcode APC.
The function then inserts the shellcode APC and the preparation APC into the APC queue of the target thread.
If the target thread is terminating, the function sets the status to STATUS_THREAD_IS_TERMINATING
.
Finally, the function cleans up by freeing any allocated memory and closing any opened handles, and then returns the status of the operation.
The function begins by looking up the target process using the process ID from the ShellcodeInfo
structure.
Next, it opens the target process and allocates memory for the shellcode.
If the memory is successfully allocated, the function writes the shellcode to the allocated memory in the target process.
The function then changes the previous mode to KernelMode
and creates a new thread in the target process using NtCreateThreadEx
, after the function is executed the previous mode is returned to the original value. The start routine of the new thread is the address of the injected shellcode.
If the thread is successfully created, the function closes the thread handle.
If the shellcode injection fails at any point, the function frees the allocated memory and closes any opened handles.
Finally, the function dereferences the target process and returns the status of the operation.
Injecting a DLL to usermode process via either APC or remote thread.
NTSTATUS MemoryUtils::InjectDllAPC(DllInformation* DllInfo)
DllInfo [DllInformation*] -- Pointer to a DllInformation structure that contains all the information regarding the injected DLL.
NTSTATUS MemoryUtils::InjectDllThread(DllInformation* DllInfo)
DllInfo [DllInformation*] -- Pointer to a DllInformation structure that contains all the information regarding the injected DLL.
# Inject DLL with remote thread
NidhoggClient.exe dllinject thread <PID> <PATH>
$ Inject DLL with APC
NidhoggClient.exe dllinject apc <PID> <PATH>
The APC injection works like the shellcode APC injection just with a template shellcode that loads then DLL into the process.
The function begins by looking up the target process using the process ID from the DllInfo
structure.
Next, it attaches to the target process and retrieves the base address of kernel32.dll
within the process.
If the base address is successfully retrieved, the function gets the address of the LoadLibraryA
function within kernel32.dll
.
The function then opens the target process and allocates memory for the DLL path.
If the memory is successfully allocated, the function writes the DLL path to the allocated memory in the target process.
The function then changes the previous mode to KernelMode
and creates a new thread in the target process using NtCreateThreadEx
, after the function is executed the previous mode is returned to the original value. The start routine of the new thread is the address of the LoadLibraryA
function, and the parameter is the address of the DLL path in the target process.
If the thread is successfully created, the function closes the thread handle.
If the DLL injection fails at any point, the function frees the allocated memory, closes any opened handles, and dereferences the target process.
Finally, the function returns the status of the operation.
Hiding the driver by removing it from the PsLoadedModuleList.
NTSTATUS MemoryUtils::HideDriver(HiddenDriverInformation* DriverInformation)
DriverInformation [HiddenDriverInformation*] -- Pointer to a HiddenDriverInformation structure that contains the driver's information.
# Hide a driver
NidhoggClient.exe driver hide <PATH>
# Unhide a driver
NidhoggClient.exe driver unhide <PATH>
The function begins by acquiring an exclusive lock on the PsLoadedModuleResource
to prevent other threads from modifying it while it is being read.
Next, it iterates over the PsLoadedModuleList
which contains all the loaded modules in the system. For each module in the list, it checks if the module name matches the driver name to be hidden.
If a match is found, it copies the original entry to a HiddenDriverItem
structure to make sure it can be restored again. It then adds the HiddenDriverItem
to the list of hidden drivers.
The function then removes the module from the PsLoadedModuleList
and sets the status to STATUS_SUCCESS
.
If the driver is not found in the PsLoadedModuleList
or if adding the HiddenDriverItem
to the list of hidden drivers fails, the function sets the status to STATUS_NOT_FOUND
or STATUS_ABANDONED
respectively.
Finally, the function releases the lock on the PsLoadedModuleResource
and returns the status of the operation.
Dumping credentials by memory scanning Lsasrv and extracting the 3DES key and encrypted credentials (password hash, username and domain). The credentials are saved in a cache and retrieved right after the dump.
NTSTATUS MemoryUtils::DumpCredentials(ULONG* AllocationSize)
AllocationSize [ULONG*] -- Pointer to a ULONG that will hold the size to allocate for the credentials buffer.
NidhoggClient.exe dump_creds
The function begins by checking if the last index of the lsass
credentials is not zero, if so it returns STATUS_ABANDONED
.
Next, it finds the process ID of lsass.exe
and looks up the process by the process ID.
The function then attaches the current thread to the address space of the lsass
process.
It locates the base address of the lsasrv.dll
module and the address of the LsaIAuditSamEvent
function within the module.
The function then finds the location of the LsaEnumerateLogonSession
and LsaInitializeProtectedMemory
functions, and the start of the LsaEnumerateLogonSession
function.
It retrieves the 3DES
key used by lsass
for encrypting credentials.
The function then finds the address of the LogonSessionList
and counts the number of credentials in the list.
It allocates memory for the credentials and then iterates over the list of credentials, copying the username, domain, and encrypted hash for each credential.
Finally, the function detaches from the lsass
process and returns the status of the operation. If the operation was successful, it also sets the count and last index of the lsass
credentials and the allocation size for the credentials buffer.