Last active
July 26, 2025 21:38
-
-
Save botder/2410c64c23ffff2fbe8029344e3f0b1f to your computer and use it in GitHub Desktop.
This piece of source code shows you how to override both RtlUserFiberStart and BaseFiberStart for fibers on the Windows operating system. This was tested on Windows 11 and all the way down to Windows Vista. Does not support ARM64 because I don't own such a device.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#define UNICODE | |
#define NOMINMAX | |
#define WIN32_NO_STATUS | |
#define WIN32_LEAN_AND_MEAN | |
#define _WIN32_WINNT 0x0A00 | |
#include <windows.h> | |
#include <windowsx.h> | |
#undef WIN32_NO_STATUS | |
#include <ntstatus.h> | |
#include <winnt.h> | |
#include <winternl.h> | |
#define NtCurrentProcess() ((HANDLE)(LONG_PTR) - 1) | |
#if defined(_WIN64) | |
#define NtGetFiberData() reinterpret_cast<FIBER*>(__readgsqword(0x20)) // NtTib.FiberData | |
#else | |
#define NtGetFiberData() reinterpret_cast<FIBER*>(__readfsdword(0x10)) // NtTib.FiberData | |
#endif | |
using FiberInitRoutine_t = VOID (WINAPI*)(); | |
struct FIBER | |
{ | |
PVOID UserData; | |
PEXCEPTION_REGISTRATION_RECORD ExceptionList; | |
PVOID StackBase; | |
PVOID StackLimit; | |
PVOID DeallocationStack; | |
CONTEXT FiberContext; | |
// ... | |
}; | |
VOID(NTAPI* pRtlExitUserThread)(NTSTATUS ExitStatus) = nullptr; | |
NTSTATUS(NTAPI* pNtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus) = nullptr; | |
VOID(NTAPI* pRtlUserFiberStart)() = nullptr; | |
VOID WINAPI CustomBaseFiberStart() | |
{ | |
std::puts(__FUNCTION__); | |
auto const fiberData = NtGetFiberData(); | |
#if defined(_WIN64) | |
auto const fiberRoutine = reinterpret_cast<LPFIBER_START_ROUTINE>(fiberData->FiberContext.Rcx); | |
auto const fiberParam = reinterpret_cast<LPVOID>(fiberData->FiberContext.Rdx); | |
#else | |
auto const fiberRoutine = reinterpret_cast<LPFIBER_START_ROUTINE>(fiberData->FiberContext.Eax); | |
auto const fiberParam = reinterpret_cast<LPVOID>(fiberData->FiberContext.Ebx); | |
#endif | |
fiberRoutine(fiberParam); | |
pRtlExitUserThread(0); | |
pNtTerminateProcess(NtCurrentProcess(), 0); | |
} | |
DECLSPEC_NORETURN VOID WINAPI CustomRtlUserFiberStart() | |
{ | |
std::puts(__FUNCTION__); | |
auto const fiberData = NtGetFiberData(); | |
#if defined(_WIN64) | |
auto const fiberBaseRoutine = reinterpret_cast<VOID(WINAPI*)()>(fiberData->FiberContext.Rax); | |
#else | |
auto const fiberBaseRoutine = reinterpret_cast<VOID(WINAPI*)()>(fiberData->FiberContext.Ecx); | |
#endif | |
fiberBaseRoutine(); | |
pRtlExitUserThread(STATUS_UNSUCCESSFUL); | |
__debugbreak(); | |
} | |
LPVOID g_mainFiber = nullptr; | |
LPVOID g_workerFiber = nullptr; | |
VOID WINAPI FiberWorker(LPVOID parameter) | |
{ | |
std::puts(__FUNCTION__); | |
if (parameter != reinterpret_cast<LPVOID>(0xDEADBEEFull)) | |
{ | |
std::fputs("Parameter for FiberWorker does not equal 0xDEADBEEF", stderr); | |
} | |
SwitchToFiber(g_mainFiber); | |
DebugBreak(); | |
} | |
int main() | |
{ | |
if (HMODULE const ntdll = GetModuleHandleW(L"ntdll.dll"); ntdll != nullptr) | |
{ | |
pRtlUserFiberStart = reinterpret_cast<decltype(pRtlUserFiberStart)>(GetProcAddress(ntdll, "RtlUserFiberStart")); | |
pRtlExitUserThread = reinterpret_cast<decltype(pRtlExitUserThread)>(GetProcAddress(ntdll, "RtlExitUserThread")); | |
pNtTerminateProcess = reinterpret_cast<decltype(pNtTerminateProcess)>(GetProcAddress(ntdll, "NtTerminateProcess")); | |
} | |
if (!pRtlUserFiberStart) | |
{ | |
std::fputs("INFO: RtlUserFiberStart not found in ntdll.dll", stderr); | |
} | |
if (!pRtlExitUserThread) | |
{ | |
std::fputs("ERROR: RtlExitUserThread not found in ntdll.dll", stderr); | |
return 1; | |
} | |
if (!pNtTerminateProcess) | |
{ | |
std::fputs("ERROR: NtTerminateProcess not found in ntdll.dll", stderr); | |
return 2; | |
} | |
std::puts("[+] Found all required functions in ntdll.dll"); | |
g_mainFiber = ConvertThreadToFiber(nullptr); | |
if (!g_mainFiber) | |
{ | |
std::fprintf(stderr, "ERROR: ConvertThreadToFiber failed with error code %#x", GetLastError()); | |
return 3; | |
} | |
std::puts("[+] Converted current thread to a fiber"); | |
g_workerFiber = CreateFiber(0, FiberWorker, reinterpret_cast<LPVOID>(0xDEADBEEFull)); | |
if (!g_workerFiber) | |
{ | |
std::fprintf(stderr, "ERROR: CreateFiber failed with error code %#x", GetLastError()); | |
return 4; | |
} | |
std::puts("[+] Created a fiber"); | |
auto fiberData = static_cast<FIBER*>(g_workerFiber); | |
#if defined(_WIN64) | |
auto pBaseFiberStart = reinterpret_cast<FiberInitRoutine_t*>(&fiberData->FiberContext.Rax); | |
auto pFiberStart = reinterpret_cast<FiberInitRoutine_t**>(&fiberData->FiberContext.Rsp); | |
#else | |
auto pBaseFiberStart = reinterpret_cast<FiberInitRoutine_t*>(&fiberData->FiberContext.Ecx); | |
auto pFiberStart = reinterpret_cast<FiberInitRoutine_t**>(&fiberData->FiberContext.Esp); | |
#endif | |
if (pRtlUserFiberStart) | |
{ | |
std::puts("[+] Overwriting RtlUserFiberStart and BaseFiberStart"); | |
// Windows 10 moved fibers code to kernelbase.dll, and introduced ntdll!RtlUserFiberStart. | |
*pBaseFiberStart = CustomBaseFiberStart; | |
**pFiberStart = CustomRtlUserFiberStart; | |
} | |
else | |
{ | |
std::puts("[+] Overwriting BaseFiberStart"); | |
**pFiberStart = CustomBaseFiberStart; | |
} | |
std::puts("[+] Switching to fiber"); | |
SwitchToFiber(g_workerFiber); | |
std::puts("[+] Returned from fiber"); | |
std::puts("[+] Deleting fiber"); | |
DeleteFiber(g_workerFiber); | |
std::puts("[+] Done"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment