Skip to content

Instantly share code, notes, and snippets.

@alfarom256
Last active August 13, 2023 12:10
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save alfarom256/fc8fab62103def18d467a25ebdad1ada to your computer and use it in GitHub Desktop.
Save alfarom256/fc8fab62103def18d467a25ebdad1ada 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;
}
@alfarom256
Copy link
Author

Note to self, sleep before publishing and revise in the morning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment