Skip to content

Instantly share code, notes, and snippets.

@iNoSec2
Forked from alfarom256/Source.cpp
Created May 8, 2023 17:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iNoSec2/a66dc499d6e8213bc917b56c0ae00504 to your computer and use it in GitHub Desktop.
Save iNoSec2/a66dc499d6e8213bc917b56c0ae00504 to your computer and use it in GitHub Desktop.
Thread Execution via NtCreateWorkerFactory
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
#define WORKER_FACTORY_FULL_ACCESS 0xf00ff
// https://github.com/winsiderss/systeminformer/blob/17fb2e0048f062a04394c4ccd615b611e6ffd45d/phnt/include/ntexapi.h#LL1096C1-L1115C52
typedef enum _WORKERFACTORYINFOCLASS
{
WorkerFactoryTimeout, // LARGE_INTEGER
WorkerFactoryRetryTimeout, // LARGE_INTEGER
WorkerFactoryIdleTimeout, // s: LARGE_INTEGER
WorkerFactoryBindingCount, // s: ULONG
WorkerFactoryThreadMinimum, // s: ULONG
WorkerFactoryThreadMaximum, // s: ULONG
WorkerFactoryPaused, // ULONG or BOOLEAN
WorkerFactoryBasicInformation, // q: WORKER_FACTORY_BASIC_INFORMATION
WorkerFactoryAdjustThreadGoal,
WorkerFactoryCallbackType,
WorkerFactoryStackInformation, // 10
WorkerFactoryThreadBasePriority, // s: ULONG
WorkerFactoryTimeoutWaiters, // s: ULONG, since THRESHOLD
WorkerFactoryFlags, // s: ULONG
WorkerFactoryThreadSoftMaximum, // s: ULONG
WorkerFactoryThreadCpuSets, // since REDSTONE5
MaxWorkerFactoryInfoClass
} WORKERFACTORYINFOCLASS, * PWORKERFACTORYINFOCLASS;
// https://github.com/winsiderss/systeminformer/blob/17fb2e0048f062a04394c4ccd615b611e6ffd45d/phnt/include/ntexapi.h#LL1212C1-L1218C64
struct _FILE_IO_COMPLETION_INFORMATION;
typedef struct _WORKER_FACTORY_DEFERRED_WORK
{
struct _PORT_MESSAGE* AlpcSendMessage;
PVOID AlpcSendMessagePort;
ULONG AlpcSendMessageFlags;
ULONG Flags;
} WORKER_FACTORY_DEFERRED_WORK, * PWORKER_FACTORY_DEFERRED_WORK;
typedef struct _WORKER_FACTORY_BASIC_INFORMATION {
LARGE_INTEGER Timeout;
LARGE_INTEGER RetryTimeout;
LARGE_INTEGER IdleTimeout;
BOOLEAN Paused;
BOOLEAN TimerSet;
BOOLEAN QueuedToExWorker;
BOOLEAN MayCreate;
BOOLEAN CreateInProgress;
BOOLEAN InsertedIntoQueue;
BOOLEAN Shutdown;
ULONG BindingCount;
ULONG ThreadMinimum;
ULONG ThreadMaximum;
ULONG PendingWorkerCount;
ULONG WaitingWorkerCount;
ULONG TotalWorkerCount;
ULONG ReleaseCount;
LONGLONG InfiniteWaitGoal;
PVOID StartRoutine;
PVOID StartParameter;
HANDLE ProcessId;
SIZE_T StackReserve;
SIZE_T StackCommit;
NTSTATUS LastThreadCreationStatus;
} WORKER_FACTORY_BASIC_INFORMATION, * PWORKER_FACTORY_BASIC_INFORMATION;
typedef NTSTATUS(WINAPI* tNtCreateWorkerFactory)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, HANDLE, HANDLE, PVOID, PVOID, ULONG, SIZE_T, SIZE_T);
typedef NTSTATUS(WINAPI* tNtWorkerFactoryWorkerReady)(HANDLE);
typedef NTSTATUS(WINAPI* tNtSetInformationWorkerFactory)(HANDLE, WORKERFACTORYINFOCLASS, PVOID, ULONG);
typedef NTSTATUS(WINAPI* tNtQueryInformationWorkerFactory)(HANDLE, WORKERFACTORYINFOCLASS, PVOID, ULONG, PULONG);
typedef NTSTATUS(WINAPI* tNtWaitForWorkViaWorkerFactory)(HANDLE, _FILE_IO_COMPLETION_INFORMATION*, ULONG, PULONG, WORKER_FACTORY_DEFERRED_WORK*);
/*
* https://github.com/DavidXanatos/TaskExplorer/blob/48c349ddf8481d4b04ede8f4564c8fc76f4a3bbc/ProcessHacker/phnt/include/ntzwapi.h#L4596
*
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwWaitForWorkViaWorkerFactory(
_In_ HANDLE WorkerFactoryHandle,
_Out_writes_to_(Count, *PacketsReturned) struct _FILE_IO_COMPLETION_INFORMATION *MiniPackets,
_In_ ULONG Count,
_Out_ PULONG PacketsReturned,
_In_ struct _WORKER_FACTORY_DEFERRED_WORK* DeferredWork
);
*/
/*
https://processhacker.sourceforge.io/doc/ntexapi_8h_source.html
NTSYSCALLAPI
NTSTATUS
NTAPI
NtCreateWorkerFactory(
_Out_ PHANDLE WorkerFactoryHandleReturn,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE CompletionPortHandle,
_In_ HANDLE WorkerProcessHandle,
_In_ PVOID StartRoutine,
_In_opt_ PVOID StartParameter,
_In_opt_ ULONG MaxThreadCount,
_In_opt_ SIZE_T StackReserve,
_In_opt_ SIZE_T StackCommit
);
*/
void test() {
MessageBoxA(NULL, "Test", "test", MB_OK);
}
int main(int argc, char** argv) {
ULONG ulThreadMinimum = 1;
ULONG ulKey = 'lain';
HANDLE hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0);
NTSTATUS status = 0; // STATUS_SUCCESS
tNtCreateWorkerFactory stubNtCreateWorkerFactory = NULL;
tNtSetInformationWorkerFactory stubNtSetInformationWorkerFactory = NULL;
tNtQueryInformationWorkerFactory stubNtQueryInformationWorkerFactory = NULL;
HANDLE hWorkerFactory = INVALID_HANDLE_VALUE;
HMODULE hNtdll = GetModuleHandleA("ntdll");
if (!hNtdll) {
return -1;
}
stubNtCreateWorkerFactory = (tNtCreateWorkerFactory)GetProcAddress(hNtdll, "NtCreateWorkerFactory");
if (!stubNtCreateWorkerFactory) {
return -1;
}
stubNtSetInformationWorkerFactory = (tNtSetInformationWorkerFactory)GetProcAddress(hNtdll, "NtSetInformationWorkerFactory");
if (!stubNtSetInformationWorkerFactory) {
return -1;
}
stubNtQueryInformationWorkerFactory = (tNtQueryInformationWorkerFactory)GetProcAddress(hNtdll, "NtQueryInformationWorkerFactory");
if (!stubNtQueryInformationWorkerFactory) {
return -1;
}
// So I'm not entirely certain if this is an actual stack?
// ProcessHacker et. al. report this as an initial CONTEXT
// but in Windbg, the PVOID lpParam provided by NtCreateWorkerFactory
// does not appear to resemble a CONTEXT, rather a true stack.
// TBD I guess?
PVOID stack = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100 * sizeof(DWORD64));
for (size_t i = 0; i < 0x100; i++) {
((PDWORD64)stack)[i] = (DWORD64)'a' + i;
}
status = stubNtCreateWorkerFactory(&hWorkerFactory, WORKER_FACTORY_FULL_ACCESS, NULL, hIocp, (HANDLE)-1, test, stack, 1, 0x1000, 0x1000);
if (!NT_SUCCESS(status)) {
printf("NtCreateWorkerFactory - %d\n", GetLastError());
return -1;
}
status = stubNtSetInformationWorkerFactory(hWorkerFactory, WorkerFactoryThreadMinimum, &ulThreadMinimum, sizeof(ULONG));
while (TRUE) {}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment