API hook monitoring under R3 starts with right-click administrator privileges

1. Basic principles

On previous operating systems, hooking CreateProcessInternel can monitor process creation. When starting as an administrator, the situation changes. Our explorer no longer calls the original path, but uses an unexported function AicFindLaunchAdminProcess.

The AicFindLaunchAdminProcess function is defined as:

ULONG_PTR WINAPI AicFindLaunchAdminProcess(
    LPWSTR lpApplicationName,
    LPWSTR lpParameters,
    DWORD UacRequestFlag,
    DWORD dwCreationFlags,
    LPWSTR lpCurrentDirectory,
    HWND hWnd,
    PVOID StartupInfo,
    PVOID ProcessInfo,
    ELEVATION_REASON* ElevationReason
);

We only need to locate the entry point of the function based on the signature and hook the function to monitor the programs started as an administrator in the resource manager.

2. Code of positioning function

Here is the entry point positioning code for this function as of Win 10:

//#include "FindLaunchAdminFunc.h"
#include <cassert>
#include <iomanip>
#include <iostream>
#include <vector>

//#pragma once


//disable nonmeaningful warnings.
#pragma warning(push)
//#pragma warning(disable: 4005) // macro redefinition
//#pragma warning(disable: 4055) // %s : from data pointer %s to function pointer %s
//#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
//#pragma comment(lib, "ntdll.lib")
#pragma warning(disable: 4005) // macro redefinition
#pragma warning(disable: 4055) // %s : from data pointer %s to function pointer %s
#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union

#include <windows.h>
#include <ntstatus.h>
#include <bcrypt.h>
//typedef PIMAGE_NT_HEADERS(NTAPI* RTLIMAGENTHEADER)(PVOID);
//HINSTANCE hNtdll = GetModuleHandleA("ntdll.dll");
//RTLIMAGENTHEADER RtlImageNtHeader = (RTLIMAGENTHEADER)GetProcAddress(hNtdll, "RtlImageNtHeader");
//
//typedef NTSTATUS(NTAPI* RTLGETVERSION)(PRTL_OSVERSIONINFOW);
//RTLGETVERSION RtlGetVersion = (RTLGETVERSION)GetProcAddress(hNtdll, "RtlGetVersion");


#define _NTDEF_
//#include <ntsecapi.h>
#undef _NTDEF_

#ifndef PAGE_SIZE
#define PAGE_SIZE 0x1000ull
#endif

#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)


typedef ULONG ELEVATION_REASON;

#define ALIGN_UP_BY(Address, Align) (((ULONG_PTR)(Address) + (Align) - 1) & amp; ~((Align) - 1))

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
ULONG_PTR AicFindLaunchAdminProcess(
_Out_ PNTSTATUS StatusCode);

#defineSUCCESS 0L
#define FAILURE_NULL_ARGUMENT 1L
#define FAILURE_API_CALL 2L
#define FAILURE_INSUFFICIENT_BUFFER 3L
#define MAX_PATH_LEN 260
#define MAX_ARGS_LEN 32768
#define UNICODE

//The following is FindLaunchAdminFunc.cpp


#define SHELL32_DLL L"shell32.dll"
#define WINDOWS_STORAGE_DLL L"windows.storage.dll"
HINSTANCE hNtdll = GetModuleHandleA("ntdll.dll");

typedef PIMAGE_NT_HEADERS(NTAPI* RTLIMAGENTHEADER)(PVOID);
RTLIMAGENTHEADER RtlImageNtHeader = (RTLIMAGENTHEADER)GetProcAddress(hNtdll, "RtlImageNtHeader");

typedef NTSTATUS(NTAPI* RTLGETVERSION)(PRTL_OSVERSIONINFOW);
RTLGETVERSION RtlGetVersion = (RTLGETVERSION)GetProcAddress(hNtdll, "RtlGetVersion");

//RTLIMAGENTHEADER RtlImageNtHeader = (RTLIMAGENTHEADER)GetProcAddress(hNtdll, "RtlImageNtHeader");
//
// AicLaunchAdminProcess prologue signature.
//

unsigned char LaunchAdminProcessSignature760x[] = {
0xFF, 0xF3, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81,
0xEC, 0x30, 0x04, 0x00, 0x00
};

unsigned char LaunchAdminProcessSignature9200[] = {
0x44, 0x89, 0x44, 0x24, 0x18, 0x53, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56,
0x41, 0x57, 0x48, 0x81, 0xEC, 0xF0, 0x03, 0x00, 0x00
};

unsigned char LaunchAdminProcessSignature9600[] = {
0x44, 0x89, 0x4C, 0x24, 0x20, 0x44, 0x89, 0x44, 0x24, 0x18, 0x53, 0x56, 0x57, 0x41,
0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81, 0xEC, 0x00, 0x04, 0x00, 0x00
};

unsigned char LaunchAdminProcessSignature10240_10586[] = {
0x40, 0x53, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81,
0xEC, 0x30, 0x04, 0x00, 0x00
};

unsigned char LaunchAdminProcessSignature14393[] = {
0x40, 0x53, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81,
0xEC, 0x20, 0x04, 0x00, 0x00
};

unsigned char LaunchAdminProcessSignature_15063_18362[] = {
0x40, 0x53, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81,
0xEC, 0x20, 0x04, 0x00, 0x00
};
//40 53 56 57 41 54 41 55 41 56 41 57 48 81 EC 30 04 00 00
unsigned char LaunchAdminProcessSignature_18363_xxxxx[] = {
0x40, 0x53, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x81,
0xEC, 0x30, 0x04, 0x00, 0x00
};


/*
* supFindPattern
*
*Purpose:
*
* Lookup pattern in buffer.
*
*/
PVOID supFindPattern(
CONST PBYTE Buffer,
SIZE_T BufferSize,
CONST PBYTE Pattern,
SIZE_T PatternSize
)
{
PBYTE p = Buffer;

if (PatternSize == 0)
return NULL;
if (BufferSize < PatternSize)
return NULL;
BufferSize -= PatternSize;

do {
p = (PBYTE)memchr(p, Pattern[0], BufferSize - (p - Buffer));
if (p == NULL)
break;

if (memcmp(p, Pattern, PatternSize) == 0)
return p;

p + + ;
} while (BufferSize - (p - Buffer) > 0); //-V555

return NULL;
}

/*
* AicFindLaunchAdminProcess
*
*Purpose:
*
* Locate unexported AppInfo routine in memory by signature.
*
*/
ULONG_PTR AicFindLaunchAdminProcess(
_Out_ PNTSTATUS StatusCode)
{
ULONG_PTR Address = 0;
PBYTE Pattern = NULL, ScanBase = NULL;
DWORD PatternSize = 0, ScanSize = 0;
IMAGE_NT_HEADERS* NtHeaders;
LPCWSTR ScanModule = NULL; // LPWSTR
RTL_OSVERSIONINFOW g_ctx{};
RtlSecureZeroMemory( & amp;g_ctx, sizeof(g_ctx));
g_ctx.dwOSVersionInfoSize = sizeof(g_ctx);
RtlGetVersion((PRTL_OSVERSIONINFOW) & amp;g_ctx);
DWORD dwBuildNumber = g_ctx.dwBuildNumber;
if (g_ctx.dwBuildNumber < 10240)
ScanModule = SHELL32_DLL;
else
ScanModule = WINDOWS_STORAGE_DLL;

switch (g_ctx.dwBuildNumber) {

case 7600:
case 7601:
Pattern = LaunchAdminProcessSignature760x;
PatternSize = sizeof(LaunchAdminProcessSignature760x);
break;
case 9200:
Pattern = LaunchAdminProcessSignature9200;
PatternSize = sizeof(LaunchAdminProcessSignature9200);
break;
case 9600:
Pattern = LaunchAdminProcessSignature9600;
PatternSize = sizeof(LaunchAdminProcessSignature9600);
break;
case 10240:
case 10586:
Pattern = LaunchAdminProcessSignature10240_10586;
PatternSize = sizeof(LaunchAdminProcessSignature10240_10586);
break;
case 14393:
Pattern = LaunchAdminProcessSignature14393;
PatternSize = sizeof(LaunchAdminProcessSignature14393);
break;
case 15063:
case 16299:
case 17134:
case 17763:
case 18362:
Pattern = LaunchAdminProcessSignature_15063_18362;
PatternSize = sizeof(LaunchAdminProcessSignature_15063_18362);
break;
case 18363:
default:
Pattern = LaunchAdminProcessSignature_18363_xxxxx;
PatternSize = sizeof(LaunchAdminProcessSignature_18363_xxxxx);
break;
}

ScanBase = (PBYTE)GetModuleHandleW(ScanModule);
if (ScanBase == NULL) {
ScanBase = (PBYTE)LoadLibraryExW(ScanModule, NULL, 0); //is in \KnownDlls
}

if (ScanBase == NULL) {
*StatusCode = STATUS_INTERNAL_ERROR;
return 0;
}

NtHeaders = RtlImageNtHeader(ScanBase);
if (NtHeaders->OptionalHeader.SizeOfImage <= PatternSize) {
*StatusCode = STATUS_INTERNAL_ERROR;
return 0;
}

ScanSize = NtHeaders->OptionalHeader.SizeOfImage - PatternSize;
Address = (ULONG_PTR)supFindPattern(ScanBase, (SIZE_T)ScanSize, Pattern, (SIZE_T)PatternSize);
if (Address == 0) {
*StatusCode = STATUS_PROCEDURE_NOT_FOUND;
return 0;
}

*StatusCode = STATUS_SUCCESS;

return Address;
}

3. Hooking and injection routines

After finding the address of the function, we can use Detours to hook the function. Our hook function is as follows:

ULONG_PTR WINAPI AicLaunchAdminProcessHook(
    LPWSTR lpApplicationName,
    LPWSTR lpParameters,
    DWORD UacRequestFlag,
    DWORD dwCreationFlags,
    LPWSTR lpCurrentDirectory,
    HWND hWnd,
    PVOID StartupInfo,
    PVOID ProcessInfo,
    ELEVATION_REASON* ElevationReason) {
    
    ULONG Str = { '\0' };
    //ULONG new_ElevationReason = 10;
    //DWORD NewUacRequestFlag = 17; //admin

    if (_wcsicmp(lpApplicationName, L"C:\Windows\System32\cmd.exe") == 0) {
        WCHAR text[128] = { 0 };
        wsprintf(text, L"The process has terminated. Located at: %s ", lpApplicationName);
        int RetSD = MessageBoxW(NULL, text, L"Hooking!CallMessage", MB_OK | MB_ICONERROR);
        returnStr;
    }
    else {
        ULONG_PTR ret = TrueAicLaunchAdminProcess(lpApplicationName, lpParameters, UacRequestFlag, dwCreationFlags, lpCurrentDirectory, hWnd, StartupInfo, ProcessInfo, ElevationReason);
        return ret;
    }
}

The complete injection code is as follows:

#include <stdlib.h>
#include <iostream>
#include <detours.h>
#include <Windows.h>
#include <TlHelp32.h>
#include <Shellapi.h>
#include <fstream>
#include <algorithm>
#include <Shlwapi.h>
#include "FindLaunchAdminFunc.h"
#pragma comment(lib,"Shlwapi.lib")
#include <cassert>

#pragma warning(disable:4996)

HMODULE s_hDll;
//shared code snippet
#pragma data_seg("SHARED")
HHOOK g_hCreateProcessHook = NULL;
BOOL g_bStopHook = FALSE;
BOOL g_bHookInstalled = FALSE;
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,RWS")

//#define WM_SHOWTASK (WM_USER + 1309)//Specialized message

using namespace std;


#define MAKEINTRESOURCE MAKEINTRESOURCEW

extern HINSTANCE hAppInstance;

ULONG_PTR(WINAPI* TrueAicLaunchAdminProcess)(
    LPWSTR lpApplicationName,
    LPWSTR lpParameters,
    DWORD UacRequestFlag,
    DWORD dwCreationFlags,
    LPWSTR lpCurrentDirectory,
    HWND hWnd,
    PVOID StartupInfo,
    PVOID ProcessInfo,
    ELEVATION_REASON* ElevationReason
    ) = NULL;


typedef ULONG_PTR(WINAPI* pfnAipFindLaunchAdminProcess)(
    LPWSTR lpApplicationName,
    LPWSTR lpParameters,
    DWORD UacRequestFlag,
    DWORD dwCreationFlags,
    LPWSTR lpCurrentDirectory,
    HWND hWnd,
    PVOID StartupInfo,
    PVOID ProcessInfo,
    ELEVATION_REASON* ElevationReason);


/**
* If lpApplicationName is powershell or cmd, append to lpParameters to run payload.
* Otherwise, copies payload to same name as app name from lpApplicationName and keeps
* original lpParameters to spoof path.
*/
ULONG_PTR WINAPI AicLaunchAdminProcessHook(
    LPWSTR lpApplicationName,
    LPWSTR lpParameters,
    DWORD UacRequestFlag,
    DWORD dwCreationFlags,
    LPWSTR lpCurrentDirectory,
    HWND hWnd,
    PVOID StartupInfo,
    PVOID ProcessInfo,
    ELEVATION_REASON* ElevationReason) {

    //ULONG new_ElevationReason = 10;
    ULONG Str = { '\0' };
    //DWORD NewUacRequestFlag = 17; //admin
    //
    if (_wcsicmp(lpApplicationName, L"C:\Users\Lenovo\Desktop\FindEXPpath.exe") == 0) {
        //wcscat_s(lpParameters, MAX_PATH_LEN, L"-c " & amp; C:\Users\Lenovo\Desktop\FindEXPpath.exe"");
        WCHAR text[128] = { 0 };
        wsprintf(text, L"The process has terminated. Located at: %s ", lpApplicationName);
        int RetSD = MessageBoxW(NULL, text, L"Hooking!CallMessage", MB_OK | MB_ICONERROR);
        returnStr;
    }
    else {
        ULONG_PTR ret = TrueAicLaunchAdminProcess(lpApplicationName, lpParameters, UacRequestFlag, dwCreationFlags, lpCurrentDirectory, hWnd, StartupInfo, ProcessInfo, ElevationReason);
        return ret;
    }
    //return new_ElevationReason;
}


//Find function address
HMODULE WINAPI ModuleFromAddress(PVOID pv) {
    MEMORY_BASIC_INFORMATION mbi;
    if (::VirtualQuery(pv, & amp;mbi, sizeof(mbi)) != 0)
        return (HMODULE)mbi.AllocationBase;
    else
        return NULL;
}



BOOL APIENTRY SetHook() {
    //return if already installed
    if (g_bHookInstalled)
       return TRUE;
    NTSTATUS ErrorCode;
    LONG error;
    PVOID LaunchAdminProcessPtr = NULL;

    if (DetourIsHelperProcess()) {
        return TRUE;
    }

    DetourRestoreAfterWith();
    LaunchAdminProcessPtr = (PVOID)AicFindLaunchAdminProcess( & amp;ErrorCode);
    TrueAicLaunchAdminProcess = (pfnAipFindLaunchAdminProcess)LaunchAdminProcessPtr;
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach( & amp;(PVOID & amp;)TrueAicLaunchAdminProcess, AicLaunchAdminProcessHook);
    error = DetourTransactionCommit();

    g_bHookInstalled = TRUE;
    return TRUE;
}


BOOL APIENTRY DropHook() {
    //Return if it has been uninstalled
    if (!g_bHookInstalled)return TRUE;

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    // Detach functions found from the export table.
    if (TrueAicLaunchAdminProcess != NULL) {
        DetourDetach( & amp;(PVOID & amp;)TrueAicLaunchAdminProcess, AicLaunchAdminProcessHook);
    }

    LONG ret = DetourTransactionCommit();
    fflush(stdout);

    g_bHookInstalled = FALSE;
    return ret == NO_ERROR;
}



static LRESULT CALLBACK CreateProcessHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
    PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;

    //wchar_t Dir[_MAX_DIR];
    wchar_t FullPath[MAX_PATH]; // [sp + 200h] [bp-614h]@1
    //wchar_t Ext[_MAX_EXT];
    //wchar_t Filename[_MAX_FNAME];
    //wchar_t Drive[_MAX_DRIVE];
    GetModuleFileNameW(0, FullPath, MAX_PATH);
    //_wsplitpath_s(FullPath, Drive, _MAX_DRIVE, Dir, _MAX_DIR, Filename, _MAX_FNAME, Ext, _MAX_EXT);

    //wstring tar = Filename;

    //transform(tar.begin(), tar.end(), tar.begin(), towlower);

    //C:\Windows\explorer.exe
    //C:\Windows\System32\Taskmgr.exe
    //if ((_wcsicmp(Filename, L"explorer") == 0)) {
    // Ignore string case search
    if (StrStrIW(FullPath, L"explorer.exe")) {

        if (!g_bStopHook)
        {
            SetHook();
        }
        else
            DropHook();
    }
    return CallNextHookEx(g_hCreateProcessHook, nCode, wParam, lParam);
}


//Export function to load global hooks
extern "C" __declspec(dllexport) BOOL SetGlobalHook() {
    g_bStopHook = FALSE;

    g_hCreateProcessHook = SetWindowsHookExW(WH_GETMESSAGE,
        CreateProcessHookProc,
        ModuleFromAddress(CreateProcessHookProc), 0);

    if (g_hCreateProcessHook == NULL) {
        wchar_t text[30];
        wsprintf(text, L"HOOK failed! error: %d.\
", GetLastError());
        MessageBox(0, text, TEXT("Error"), MB_OK);
        return false;
    }
    return TRUE;
}

//Export function to uninstall global hooks
extern "C" __declspec(dllexport) BOOL DropGlobalHook() {
    //DropHook();
    g_bStopHook = TRUE;
    Sleep(3000);
    UnhookWindowsHookEx(g_hCreateProcessHook);
    
    return TRUE;
}


BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved
) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        s_hDll = hModule;
        DisableThreadLibraryCalls(hModule);
        //DropGlobalHook();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        //DropGlobalHook();
        
        break;
    }
    return TRUE;
}

4. Screenshot of running effect

Updated on: 2023.10.24