"Fever Dream" - Code executing when the Windows machine is locked

This code is called "Fever Dream" because I write it when I had a fever of 102.8f (39.3c) from the Influenza virus. I got tired of laying in bed non-stop. I tried to be at least a little productive.

This is a simple proof-of-concept demonstrating using WTSRegisterSessionNotification to receive message notifications when the machine is locked and unlocked from the user.

In this particular example, when the machine is locked the code checks for the existence of a thread (or it's current execution state) by invoking GetExitCodeThread. If GetExitCodeThread returns STILL_ACTIVE, thread execution resumes. Otherwise, the thread is created (presumably for the first time).

The thread called "WtsSessionLockedThread" is where the payload would go. My code here is filler — an infinite loop that invokes Sleep for 100 milliseconds.

When the machine receives an unlock message the code checks the current status of the thread (STILL_ACTIVE). If it's still active, it suspends execution.

I plan on expanding the code base, I want to try to get "lower" and making use of NTDLL and WIN32U functionality. Nothing special, but an interesting trick and maybe a neat anti-debug technique.

#include <windows.h>
#include <wtsapi32.h>

#pragma comment(lib, "Wtsapi32.lib")

BOOL WtsRegistrationActive = FALSE;
HANDLE hThread = NULL;

LPTHREAD_START_ROUTINE WtsSessionLockedThread(VOID)
{
    for (DWORD dwX = 0;;dwX++)
    {
        Sleep(100);
    }

    return ERROR_SUCCESS;
}

LRESULT CALLBACK WindowMessageReceiveRoutine(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
        case WM_CREATE:
        {
            if (!WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION))
                PostMessageW(hWnd, WM_DESTROY, 0, 0);
            else
                WtsRegistrationActive = TRUE;

            break;
        }

        case WM_WTSSESSION_CHANGE:
        {
            ThreadState = 0;

            switch (wParam)
            {
                case WTS_SESSION_LOCK:
                {
                    GetExitCodeThread(hThread, &ThreadState);
                    
                    if (ThreadState == STILL_ACTIVE)
                        ResumeThread(hThread);
                    else
                    {
                        hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WtsSessionLockedThread, NULL, 0, NULL);
                        if (hThread == NULL)
                            PostMessageW(hWnd, WM_DESTROY, 0, 0);
                    }
                
                    break;
                }

                case WTS_SESSION_UNLOCK:
                {
                    GetExitCodeThread(hThread, &ThreadState);

                    if (ThreadState == STILL_ACTIVE)
                        SuspendThread(hThread);
                
                    break;
                }
                
                default:
                    break;
            }

            break;
        }
            
        case WM_DESTROY:
        {
            if (WtsRegistrationActive)
                WTSUnRegisterSessionNotification(hWnd);

#pragma warning( push )
#pragma warning( disable : 6258)
            if (hThread)
                TerminateThread(hThread, 0);
#pragma warning( pop ) 

            break;
        }
            
        default:
            return DefWindowProcW(hWnd, Msg, wParam, lParam);
    }


    return ERROR_SUCCESS;
}

INT WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ INT nShowCmd)
{
    BOOL bFlag = FALSE;
    DWORD dwError = ERROR_SUCCESS;
    WNDCLASSEXW Wnd = { 0 };
    MSG Message = { 0 };

    Wnd.cbSize = sizeof(WNDCLASSEXW);
    Wnd.lpfnWndProc = WindowMessageReceiveRoutine;
    Wnd.hInstance = hInstance;
    Wnd.lpszClassName = L"TestClass";

    if (!RegisterClassExW(&Wnd))
        goto EXIT_ROUTINE;

    if (CreateWindowExW(0, L"TestClass", L"", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL) == NULL)
        goto EXIT_ROUTINE;

    while ((bFlag = GetMessageW(&Message, NULL, 0, 0)) != FALSE)
    {
        if (bFlag == -1)
            goto EXIT_ROUTINE;

        TranslateMessage(&Message);
        DispatchMessageW(&Message);
    }

    bFlag = TRUE;

EXIT_ROUTINE:

    if (!bFlag)
        dwError = GetLastError();

    return dwError;
}

Last updated