#include "Win-Ops-Master.h"
#include "NtDefine.h"
#include <sddl.h>
#include <iostream>
#include <threadpoolapiset.h>
#include <random>

DWORD LastError = 0;

OpsMaster::OpsMaster()
{
	LoadLibraryW(L"ntdll.dll");
	HMODULE hm = GetModuleHandleW(L"ntdll.dll");
	_NtRaiseHardError = (NTSTATUS(WINAPI*)(NTSTATUS ErrorStactus, ULONG NumberOfParameters,
		ULONG UnicodeStringParameterMask, PULONG_PTR * Parameters, ULONG ValidResponseOption, PULONG Response))GetProcAddress(hm, "NtRaiseHardError");
	_RtlAdjustPrivilege = (NTSTATUS(WINAPI*) (ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled))GetProcAddress(hm, "RtlAdjustPrivilege");
	_NtSetInformationFile = (NTSTATUS(WINAPI*) (HANDLE FileHandle,
		PIO_STATUS_BLOCK IoStatusBlock,
		PVOID FileInformation,
		ULONG Length,
		FILE_INFORMATION_CLASS FileInformationClass))GetProcAddress(hm, "NtSetInformationFile");
	_RtlNtStatusToDosError = (ULONG(WINAPI*) (NTSTATUS Status))GetProcAddress(hm, "RtlNtStatusToDosError");
	_RtlInitUnicodeString = (NTSTATUS(WINAPI*)(PUNICODE_STRING, PCWSTR)) GetProcAddress(hm, "RtlInitUnicodeString");
	_NtCreateSymbolicLinkObject = (NTSTATUS(WINAPI*)(
		OUT PHANDLE             pHandle,
		IN ACCESS_MASK          DesiredAccess,
		IN POBJECT_ATTRIBUTES   ObjectAttributes,
		IN PUNICODE_STRING      DestinationName))GetProcAddress(hm, "NtCreateSymbolicLinkObject");
	_NtCreateFile = (NTSTATUS(WINAPI*)(
		PHANDLE            FileHandle,
		ACCESS_MASK        DesiredAccess,
		POBJECT_ATTRIBUTES ObjectAttributes,
		PIO_STATUS_BLOCK   IoStatusBlock,
		PLARGE_INTEGER     AllocationSize,
		ULONG              FileAttributes,
		ULONG              ShareAccess,
		ULONG              CreateDisposition,
		ULONG              CreateOptions,
		PVOID              EaBuffer,
		ULONG              EaLength))GetProcAddress(hm, "NtCreateFile");
	_NtSetSecurityObject = (NTSTATUS(WINAPI*)(
		HANDLE               Handle,
		SECURITY_INFORMATION SecurityInformation,
		PSECURITY_DESCRIPTOR SecurityDescriptor
		))GetProcAddress(hm, "NtSetSecurityObject");
	_NtOpenProcess = (NTSTATUS(WINAPI*)(
		PHANDLE            ProcessHandle,
		ACCESS_MASK        DesiredAccess,
		POBJECT_ATTRIBUTES ObjectAttributes,
		PCLIENT_ID         ClientId
		))GetProcAddress(hm, "NtOpenProcess");
	_NtTerminateProcess = (NTSTATUS(WINAPI*)(
		IN HANDLE               ProcessHandle OPTIONAL,
		IN NTSTATUS             ExitStatus
		))GetProcAddress(hm, "NtTerminateProcess");
	_NtClose = (NTSTATUS(WINAPI*)(HANDLE Handle))GetProcAddress(hm, "NtClose");
	_NtDeviceIoControlFile = (NTSTATUS(WINAPI*)(
		HANDLE           FileHandle,
		HANDLE           Event,
		PIO_APC_ROUTINE  ApcRoutine,
		PVOID            ApcContext,
		PIO_STATUS_BLOCK IoStatusBlock,
		ULONG            IoControlCode,
		PVOID            InputBuffer,
		ULONG            InputBufferLength,
		PVOID            OutputBuffer,
		ULONG            OutputBufferLength)) GetProcAddress(hm, "NtDeviceIoControlFile");
	_NtCreateDirectoryObjectEx = (NTSTATUS(WINAPI*)(
		PHANDLE, ACCESS_MASK,
		POBJECT_ATTRIBUTES, HANDLE, BOOLEAN
		))GetProcAddress(hm, "NtCreateDirectoryObjectEx");
	_NtOpenDirectoryObject = (NTSTATUS(WINAPI*)(
		PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES
		))GetProcAddress(hm, "NtOpenDirectoryObject");
	_NtWriteFile = (NTSTATUS(WINAPI*)
		(HANDLE           FileHandle,
			HANDLE           Event,
			PIO_APC_ROUTINE  ApcRoutine,
			PVOID            ApcContext,
			PIO_STATUS_BLOCK IoStatusBlock,
			PVOID            Buffer,
			ULONG            Length,
			PLARGE_INTEGER   ByteOffset,
			PULONG           Key)) GetProcAddress(hm, "NtWriteFile");
	_NtWaitForSingleObject = (NTSTATUS(WINAPI*)(
		IN HANDLE               ObjectHandle,
		IN BOOLEAN              Alertable,
		IN PLARGE_INTEGER       TimeOut OPTIONAL)) GetProcAddress(hm, "NtWaitForSingleObject");
	_NtReadFile = (NTSTATUS(WINAPI*)(
		_In_     HANDLE           FileHandle,
		_In_opt_ HANDLE           Event,
		_In_opt_ PIO_APC_ROUTINE  ApcRoutine,
		_In_opt_ PVOID            ApcContext,
		_Out_    PIO_STATUS_BLOCK IoStatusBlock,
		_Out_    PVOID            Buffer,
		_In_     ULONG            Length,
		_In_opt_ PLARGE_INTEGER   ByteOffset,
		_In_opt_ PULONG           Key
		))GetProcAddress(hm, "NtReadFile");
	_NtCompareTokens = (NTSTATUS(WINAPI*)(
		_In_  HANDLE   FirstTokenHandle,
		_In_  HANDLE   SecondTokenHandle,
		_Out_ PBOOLEAN Equal
		)) GetProcAddress(hm, "NtCompareTokens");
	_ZwDeleteFile = (NTSTATUS(WINAPI*)
		(POBJECT_ATTRIBUTES ObjectAttributes))GetProcAddress(hm, "ZwDeleteFile");
	_ZwCreateKey = (NTSTATUS(WINAPI*)
		(PHANDLE            KeyHandle,
			ACCESS_MASK        DesiredAccess,
			POBJECT_ATTRIBUTES ObjectAttributes,
			ULONG              TitleIndex,
			PUNICODE_STRING    Class,
			ULONG              CreateOptions,
			PULONG             Disposition))GetProcAddress(hm, "ZwCreateKey");
	_ZwDeleteKey = (NTSTATUS(WINAPI*)
		(HANDLE KeyHandle))GetProcAddress(hm, "ZwDeleteKey");
	_ZwSetValueKey = (NTSTATUS(WINAPI*)(
		HANDLE          KeyHandle,
		PUNICODE_STRING ValueName,
		ULONG           TitleIndex,
		ULONG           Type,
		PVOID           Data,
		ULONG           DataSize
		))GetProcAddress(hm, "ZwSetValueKey");
	_NtSuspendProcess = (NTSTATUS(WINAPI*)(
		HANDLE ProcessHandle
		))GetProcAddress(hm, "NtSuspendProcess");
	_NtResumeProcess = (NTSTATUS(WINAPI*)(
		HANDLE ProcessHandle
		))GetProcAddress(hm, "NtResumeProcess");
	_ZwMakeTemporaryObject = (NTSTATUS(WINAPI*)(
		HANDLE Handle
		))GetProcAddress(hm, "ZwMakeTemporaryObject");
	_ZwMakePermanentObject = (NTSTATUS(WINAPI*)(
		HANDLE Handle
		))GetProcAddress(hm, "ZwMakePermanentObject");
	/*_NtOpenSymbolicLinkObject = (NTSTATUS(WINAPI*)(
		_Out_ PHANDLE            LinkHandle,
		_In_  ACCESS_MASK        DesiredAccess,
		_In_  POBJECT_ATTRIBUTES ObjectAttributes
		))GetProcAddress(hm, "NtOpenSymbolicLinkObject");*/

	return;
}
std::wstring BuildNativePath(std::wstring path) {
	//I am considering any path that start with \ is a native path
	if (path.rfind(L"\\", 0) != std::wstring::npos)
		return path;
	path = L"\\??\\" + path;
	return path;
}

DWORD OpsMaster::NtStatusToDOS(NTSTATUS status) {
	return _RtlNtStatusToDosError(status);
}

DWORD OpsMaster::GetLastErr() {
	return LastError;
}

void SetLastErr(DWORD err) {
	LastError = err;
}

bool OpsMaster::MoveByHandle(HANDLE hfile, std::wstring target)
{
	target = BuildNativePath(target);
	size_t buffer_sz = sizeof(FILE_RENAME_INFO) + (target.size() * sizeof(wchar_t));
	FILE_RENAME_INFO* rename_info = (FILE_RENAME_INFO*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_sz);
	rename_info->ReplaceIfExists = TRUE;
	rename_info->RootDirectory = NULL;
	rename_info->Flags = 0x00000001 | 0x00000002 | 0x00000040;
	rename_info->FileNameLength = target.size() * sizeof(wchar_t);
	memcpy(&rename_info->FileName[0], target.c_str(), target.size() * sizeof(wchar_t));
	IO_STATUS_BLOCK io = { 0 };
	NTSTATUS status = _NtSetInformationFile(hfile, &io, rename_info, buffer_sz, FileRenameInformationEx);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	HeapFree(GetProcessHeap(), NULL, rename_info);
	SetLastErr(GetLastError());
	return true;
}

bool OpsMaster::MoveByHandle(HANDLE hfile, std::string target) {
	return OpsMaster::MoveByHandle(hfile, std::wstring(target.begin(), target.end()));
}


HANDLE OpsMaster::CreateNativeSymlink(std::wstring link, std::wstring target) {
	HANDLE ret;
	UNICODE_STRING ulnk;
	UNICODE_STRING utarget;
	NTSTATUS status;

	status = _RtlInitUnicodeString(&ulnk, link.c_str());
	if ((status != STATUS_SUCCESS) && (status != (NTSTATUS)ulnk.MaximumLength)) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}

	status = _RtlInitUnicodeString(&utarget, target.c_str());
	if ((status != STATUS_SUCCESS) && (status != (NTSTATUS)utarget.MaximumLength)) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}

	OBJECT_ATTRIBUTES objattr;
	InitializeObjectAttributes(&objattr, &ulnk, OBJ_CASE_INSENSITIVE, nullptr, nullptr);

	NTSTATUS stat = _NtCreateSymbolicLinkObject(&ret, SYMBOLIC_LINK_ALL_ACCESS,
		&objattr, &utarget);

	if (stat != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(stat));
		return nullptr;
	}
	return ret;
}

HANDLE OpsMaster::CreateNativeSymlink(std::string link, std::string target) {
	return OpsMaster::CreateNativeSymlink(std::wstring(link.begin(), link.end()),
		std::wstring(target.begin(), target.end()));
}
bool OpsMaster::CreateDosDeviceLink(std::string link, std::string target) {
	return OpsMaster::CreateDosDeviceLink(
		std::wstring(link.begin(), link.end()),
		std::wstring(target.begin(), target.end()));
}

bool OpsMaster::CreateDosDeviceLink(std::wstring link, std::wstring target)
{
	if (link[0] == L'\\') {
		link = L"Global\\GLOBALROOT" + link;
	}
	target = BuildNativePath(target);
	DWORD LastErr = GetLastError();
	if (DefineDosDeviceW(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, link.c_str(), target.c_str()))
	{
		SetLastErr(GetLastError());
		SetLastError(LastErr);
		return true;
	}
	SetLastErr(GetLastError());
	SetLastError(LastErr);
	return false;
}

bool OpsMaster::RemoveDosDeviceLink(std::string link)
{
	return OpsMaster::RemoveDosDeviceLink(std::wstring(link.begin(), link.end()));
}

bool OpsMaster::RemoveDosDeviceLink(std::wstring link)
{
	if (link[0] == L'\\') {
		link = L"Global\\GLOBALROOT" + link;
	}
	DWORD LastErr = GetLastError();
	if (DefineDosDeviceW(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH |
		DDD_REMOVE_DEFINITION,
		link.c_str(), NULL))
	{
		SetLastErr(GetLastError());
		SetLastError(LastErr);
		return true;
	}
	SetLastErr(GetLastError());
	SetLastError(LastErr);
	return false;
}

HANDLE OpsMaster::OpenDirectory(std::wstring directory, DWORD access_mask, DWORD share_mode, DWORD creation_disposition, DWORD flags)
{
	directory = BuildNativePath(directory);
	HANDLE h;
	OBJECT_ATTRIBUTES objattr;
	UNICODE_STRING target;
	IO_STATUS_BLOCK io;
	NTSTATUS status;
	_RtlInitUnicodeString(&target, directory.c_str());
	InitializeObjectAttributes(&objattr, &target, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
	switch (creation_disposition) {
	case CREATE_NEW:
		status = _NtCreateFile(&h, access_mask, &objattr, &io, NULL, FILE_ATTRIBUTE_NORMAL, share_mode,
			FILE_CREATE, flags ? FILE_DIRECTORY_FILE | flags : FILE_DIRECTORY_FILE | FILE_FLAG_OPEN_REPARSE_POINT, NULL, NULL);
		break;
	case OPEN_EXISTING:
		status = _NtCreateFile(&h, access_mask, &objattr, &io, NULL, FILE_ATTRIBUTE_NORMAL, share_mode,
			FILE_OPEN, flags ? FILE_DIRECTORY_FILE | flags : FILE_DIRECTORY_FILE | FILE_FLAG_OPEN_REPARSE_POINT, NULL, NULL);
		break;
	default:
		status = _NtCreateFile(&h, access_mask, &objattr, &io, NULL, FILE_ATTRIBUTE_NORMAL, share_mode,
			FILE_OPEN_IF, flags ? FILE_DIRECTORY_FILE | flags : FILE_DIRECTORY_FILE | FILE_FLAG_OPEN_REPARSE_POINT, NULL, NULL);

	}
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}
	return h;
}

HANDLE OpsMaster::OpenDirectory(std::string directory, DWORD access_mask, DWORD share_mode, DWORD creation_disposition, DWORD flags)
{
	return OpsMaster::OpenDirectory(std::wstring(directory.begin(),
		directory.end()), access_mask, share_mode,
		creation_disposition);
}

HANDLE OpsMaster::OpenFileNative(std::wstring file, DWORD access_mask, DWORD share_mode, DWORD creation_dispostion, DWORD flags)
{
	file = BuildNativePath(file);
	access_mask |= SYNCHRONIZE;
	OBJECT_ATTRIBUTES objattr;
	UNICODE_STRING target;
	IO_STATUS_BLOCK io;
	_RtlInitUnicodeString(&target, file.c_str());
	InitializeObjectAttributes(&objattr, &target, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
	NTSTATUS status = -1;
	HANDLE ret = 0;
	switch (creation_dispostion) {
	case OPEN_EXISTING:
		status = _NtCreateFile(&ret, access_mask, &objattr, &io, NULL, NULL, share_mode, FILE_OPEN,
			flags ? flags : (FILE_NON_DIRECTORY_FILE | flags), NULL, NULL);
		break;
	case OPEN_ALWAYS:
		status = _NtCreateFile(&ret, access_mask, &objattr, &io, NULL, NULL, share_mode, FILE_OPEN_IF,
			flags ? flags : (FILE_NON_DIRECTORY_FILE | flags), NULL, NULL);
		break;
	case CREATE_ALWAYS:
		status = _NtCreateFile(&ret, access_mask, &objattr, &io, NULL, NULL, share_mode, FILE_OVERWRITE_IF,
			flags ? flags : (FILE_NON_DIRECTORY_FILE | flags), NULL, NULL);
		break;
	case CREATE_NEW:
		status = _NtCreateFile(&ret, access_mask, &objattr, &io, NULL, NULL, share_mode, FILE_CREATE,
			flags ? flags : (FILE_NON_DIRECTORY_FILE | flags), NULL, NULL);
		break;
	case TRUNCATE_EXISTING:
		status = _NtCreateFile(&ret, access_mask, &objattr, &io, NULL, NULL, share_mode, FILE_OVERWRITE,
			FILE_NON_DIRECTORY_FILE | flags, NULL, NULL);
		break;
	}
	if (status != STATUS_SUCCESS)
		SetLastErr(_RtlNtStatusToDosError(status));
	return ret;
}

HANDLE OpsMaster::OpenFileNative(std::string file, DWORD access_mask, DWORD share_mode, DWORD creation_dispostion, DWORD flags)
{
	return OpsMaster::OpenFileNative(std::wstring(file.begin(), file.end()),
		access_mask, share_mode, creation_dispostion);
}

bool OpsMaster::WriteFileNative(HANDLE hfile, PVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten)
{
	IO_STATUS_BLOCK io;
	LARGE_INTEGER li;
	li.LowPart = 0;
	li.HighPart = 0;
	NTSTATUS status = _NtWriteFile(hfile, NULL,
		NULL, NULL, &io, lpBuffer, nNumberOfBytesToWrite, &li, NULL);
	if (status == STATUS_PENDING)
		status = _NtWaitForSingleObject(hfile, FALSE, NULL);
	if (lpNumberOfBytesWritten != nullptr)
		*lpNumberOfBytesWritten = io.Information;
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}

	return true;
}

bool OpsMaster::ReadFileNative(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead)
{
	if (nNumberOfBytesToRead == 0) {
		LARGE_INTEGER sz;
		GetFileSizeEx(hFile, &sz);
		nNumberOfBytesToRead = sz.QuadPart;
	}
	LARGE_INTEGER offset;
	offset.LowPart = 0;
	offset.HighPart = 0;
	IO_STATUS_BLOCK io;
	NTSTATUS status = _NtReadFile(hFile, NULL, NULL, NULL,
		&io, lpBuffer, nNumberOfBytesToRead, &offset, NULL);
	if (status == STATUS_PENDING)
		status = _NtWaitForSingleObject(hFile, FALSE, NULL);

	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	if (lpNumberOfBytesRead != nullptr)
		*lpNumberOfBytesRead = io.Information;
	return true;
}

HANDLE OpsMaster::OpenProcessNative(DWORD PID, DWORD access_mask)
{
	HANDLE hret = nullptr;
	OBJECT_ATTRIBUTES objattr;
	CLIENT_ID id = { (HANDLE)PID,0 };
	InitializeObjectAttributes(&objattr, 0, 0, 0, 0);
	NTSTATUS status = _NtOpenProcess(&hret, access_mask, &objattr, &id);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
	}
	return hret;
}

bool OpsMaster::SuspendProcess(HANDLE hprocess)
{
	NTSTATUS status = _NtSuspendProcess(hprocess);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::ResumeProcess(HANDLE hprocess)
{
	NTSTATUS status = _NtResumeProcess(hprocess);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::TerminateProcessNative(DWORD process_id)
{
	CLIENT_ID id = { (HANDLE)process_id, 0 };
	OBJECT_ATTRIBUTES objattr;
	InitializeObjectAttributes(&objattr, 0, 0, 0, 0);
	HANDLE proc;
	NTSTATUS status = _NtOpenProcess(&proc, PROCESS_TERMINATE, &objattr, &id);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	status = _NtTerminateProcess(proc, STATUS_SUCCESS);
	_NtClose(proc);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}
bool OpsMaster::TerminateProcessNative(HANDLE hprocess)
{
	NTSTATUS status = _NtTerminateProcess(hprocess, STATUS_SUCCESS);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

HANDLE OpsMaster::SetTokenDosDevice(std::wstring device_path, HANDLE htoken)
{
	if (htoken != NULL) {
		ImpersonateLoggedOnUser(htoken);
	}
	HANDLE hret = OpsMaster::CreateNativeSymlink(std::wstring(L"\\??\\c:"), std::wstring(device_path));
	if (htoken != NULL) {
		RevertToSelf();
	}
	return hret;
}

HANDLE OpsMaster::SetTokenDosDevice(std::string device_path, HANDLE htoken)
{
	return OpsMaster::SetTokenDosDevice(std::wstring(device_path.begin(), device_path.end()),
		htoken);
}

void OpsMaster::bsod() {
	BOOLEAN b;
	ULONG r;
	_RtlAdjustPrivilege(19, true, false, &b);
	_NtRaiseHardError(0xDeadDead, 0, 0, 0, 6, &r);
	return;
}

HANDLE OpsMaster::OpenNamedPipe(std::wstring pipe_name, DWORD desired_access, DWORD impersonation_level)
{

	UNICODE_STRING pipe;
	OBJECT_ATTRIBUTES objattr;
	HANDLE ret;
	IO_STATUS_BLOCK io;
	_RtlInitUnicodeString(&pipe, pipe_name.c_str());
	InitializeObjectAttributes(&objattr, &pipe, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
	DWORD dwattr = FILE_ATTRIBUTE_NORMAL | SECURITY_SQOS_PRESENT | impersonation_level;
	NTSTATUS status = _NtCreateFile(&ret, desired_access, &objattr, &io, NULL, dwattr, ALL_SHARING,
		FILE_OPEN, NULL, NULL, NULL);

	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}
	return ret;
}

HANDLE OpsMaster::OpenNamedPipe(std::string pipe_name, DWORD desired_access, DWORD impersonation_level)
{
	return OpsMaster::OpenNamedPipe(std::wstring(pipe_name.begin(), pipe_name.end()),
		desired_access, impersonation_level);
}

bool OpsMaster::CreateNativeHardLink(HANDLE hfile, std::wstring target)
{
	target = BuildNativePath(target);
	IO_STATUS_BLOCK io;

	FILE_LINK_INFORMATION* link_inf = (FILE_LINK_INFORMATION*)_malloca(sizeof(FILE_LINK_INFORMATION) + (target.size() * sizeof(WCHAR)));

	memset(link_inf, 0, sizeof(FILE_LINK_INFORMATION) + (target.size() * sizeof(WCHAR)));

	link_inf->FileNameLength = target.size() * sizeof(WCHAR);
	link_inf->ReplaceIfExists = TRUE;
	link_inf->RootDirectory = NULL;
	memcpy(&link_inf->FileName[0], target.c_str(), target.size() * sizeof(WCHAR));

	NTSTATUS status = _NtSetInformationFile(hfile, &io, link_inf, sizeof(FILE_LINK_INFORMATION) + (target.size() * sizeof(WCHAR)), FileLinkInformation);
	free(link_inf);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::CreateNativeHardLink(HANDLE hfile, std::string target)
{
	return OpsMaster::CreateNativeHardLink(hfile, std::wstring(target.begin(), target.end()));
}

bool OpsMaster::CreateNativeHardLink(std::wstring link, std::wstring target)
{

	// Before windows hardlink mitigation, only GENERIC_READ is required to create the hardlink
	// but since the mitigation, WRITE_ATTRIBUTES is now required to create a hardlink
	// the best solution for me here is to open the file with MAXIMUM_ALLOWED
	HANDLE hf = OpsMaster::OpenFileNative(link);
	bool ret = OpsMaster::CreateNativeHardLink(hf, target);
	_NtClose(hf);
	return ret;
}

bool OpsMaster::CreateNativeHardLink(std::string link, std::string target)
{
	// Before windows hardlink mitigation, only GENERIC_READ is required to create the hardlink
	// but since the mitigation, WRITE_ATTRIBUTES is now required to create a hardlink
	// the best solution for me here is to open the file with MAXIMUM_ALLOWED
	HANDLE hf = OpsMaster::OpenFileNative(link);
	bool ret = OpsMaster::CreateNativeHardLink(hf, target);
	_NtClose(hf);
	return ret;
}

bool OpsMaster::CreateMountPoint(HANDLE hdir, std::wstring target, std::wstring printname)
{

	target = BuildNativePath(target);
	size_t targetsz = target.size() * 2;
	size_t printnamesz = printname.size() * 2;
	size_t pathbuffersz = targetsz + printnamesz + 12;
	size_t totalsz = pathbuffersz + REPARSE_DATA_BUFFER_HEADER_LENGTH;
	REPARSE_DATA_BUFFER* rdb = (REPARSE_DATA_BUFFER*)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, totalsz);
	//memset(rdb, 0, totalsz);
	rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
	rdb->ReparseDataLength = static_cast<USHORT>(pathbuffersz);
	rdb->Reserved = NULL;
	rdb->MountPointReparseBuffer.SubstituteNameOffset = NULL;
	rdb->MountPointReparseBuffer.SubstituteNameLength = static_cast<USHORT>(targetsz);
	memcpy(rdb->MountPointReparseBuffer.PathBuffer, target.c_str(), targetsz + 2);
	rdb->MountPointReparseBuffer.PrintNameOffset = static_cast<USHORT>(targetsz + 2);
	rdb->MountPointReparseBuffer.PrintNameLength = static_cast<USHORT>(printnamesz);
	memcpy(rdb->MountPointReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printnamesz + 2);
	DWORD cb = 0;
	OVERLAPPED ov = { 0 };
	HANDLE hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	ov.hEvent = hevent;
	bool ret = false;
	DeviceIoControl(hdir, FSCTL_SET_REPARSE_POINT, rdb, totalsz, NULL, NULL, NULL, &ov);
	if (GetLastError() == ERROR_SUCCESS)
		ret = true;
	if (GetLastError() == ERROR_IO_PENDING) {
		GetOverlappedResult(hdir, &ov, &cb, TRUE);
		if (ov.Internal == STATUS_SUCCESS)
			ret = true;
		else
			SetLastErr(_RtlNtStatusToDosError(ov.Internal));
	}
	_NtClose(hevent);
	HeapFree(GetProcessHeap(), NULL, rdb);
	return ret;
}

bool OpsMaster::CreateMountPoint(HANDLE hdir, std::string target, std::string printname)
{
	return OpsMaster::CreateMountPoint(hdir, std::wstring(target.begin(), target.end()),
		std::wstring(printname.begin(), printname.end()));
}

bool OpsMaster::CreateMountPoint(std::wstring dir, std::wstring target, std::wstring printname)
{

	HANDLE hdir = OpenDirectory(dir, FILE_WRITE_DATA, ALL_SHARING, OPEN_ALWAYS);
	bool ret = CreateMountPoint(hdir, target, printname);
	_NtClose(hdir);
	return ret;
}

bool OpsMaster::CreateMountPoint(std::string dir, std::string target, std::string printname)
{
	return OpsMaster::CreateMountPoint(std::wstring(dir.begin(), dir.end()),
		std::wstring(target.begin(), target.end()), std::wstring(printname.begin(), printname.end()));
}

bool OpsMaster::DeleteMountPoint(HANDLE hdir)
{
	REPARSE_GUID_DATA_BUFFER rp_buffer = { 0 };
	rp_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
	DWORD cb = 0;
	OVERLAPPED ov = { 0 };
	HANDLE hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	ov.hEvent = hevent;
	bool ret = false;
	DeviceIoControl(hdir, FSCTL_DELETE_REPARSE_POINT, &rp_buffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
		nullptr, NULL, &cb, &ov);
	if (GetLastError() == ERROR_SUCCESS)
		ret = true;
	if (GetLastError() == ERROR_IO_PENDING) {
		GetOverlappedResult(hdir, &ov, &cb, TRUE);
		if (ov.Internal == STATUS_SUCCESS)
			ret = true;
		else
			SetLastErr(_RtlNtStatusToDosError(ov.Internal));
	}
	_NtClose(hevent);
	return ret;
}

bool OpsMaster::DeleteMountPoint(std::wstring dir)
{
	HANDLE hdir = OpenDirectory(dir, FILE_WRITE_DATA, ALL_SHARING, OPEN_EXISTING);
	bool rt = OpsMaster::DeleteMountPoint(hdir);
	_NtClose(hdir);
	return rt;
}

bool OpsMaster::DeleteMountPoint(std::string dir)
{
	HANDLE hdir = OpenDirectory(dir, FILE_WRITE_DATA, ALL_SHARING, OPEN_EXISTING);
	bool rt = OpsMaster::DeleteMountPoint(hdir);
	_NtClose(hdir);
	return rt;
}

std::wstring OpsMaster::GetMountPointData(HANDLE hdir, std::wstring)
{
	REPARSE_DATA_BUFFER* rdb = (REPARSE_DATA_BUFFER*)_malloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);

	DWORD rd = 0;

	if (!DeviceIoControl(hdir, FSCTL_GET_REPARSE_POINT, NULL,
		NULL, rdb, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &rd, nullptr))
		return L"";
	WCHAR* bs = &rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.SubstituteNameOffset / 2];
	std::wstring ret = std::wstring(bs, bs + (rdb->MountPointReparseBuffer.SubstituteNameLength / 2));
	free(rdb);
	return ret;
}

std::string OpsMaster::GetMountPointData(HANDLE hdir, std::string)
{
	std::wstring rt = OpsMaster::GetMountPointData(hdir, L"");
	return std::string(rt.begin(), rt.end());
}

std::wstring OpsMaster::GetMountPointData(std::wstring dir)
{
	HANDLE hdir = OpsMaster::OpenDirectory(dir, GENERIC_READ, ALL_SHARING, OPEN_EXISTING);
	std::wstring ret = OpsMaster::GetMountPointData(hdir, L"");
	_NtClose(hdir);
	return ret;
}

std::string OpsMaster::GetMountPointData(std::string dir)
{
	HANDLE hdir = OpsMaster::OpenDirectory(dir, GENERIC_READ, ALL_SHARING, OPEN_EXISTING);
	std::string ret = OpsMaster::GetMountPointData(hdir, "");
	_NtClose(hdir);
	return ret;
}

HANDLE OpsMaster::CreateObjDir(std::wstring dir)
{
	HANDLE rt = NULL;
	OBJECT_ATTRIBUTES objattr;
	UNICODE_STRING target;
	_RtlInitUnicodeString(&target, dir.c_str());
	InitializeObjectAttributes(&objattr, &target, OBJ_CASE_INSENSITIVE, nullptr, nullptr);

	NTSTATUS status = _NtCreateDirectoryObjectEx(&rt, MAXIMUM_ALLOWED, &objattr, nullptr, FALSE);


	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}
	return rt;
}

HANDLE OpsMaster::CreateObjDir(std::string dir)
{
	return OpsMaster::CreateObjDir(std::wstring(dir.begin(), dir.end()));
}

HANDLE OpsMaster::OpenObjDir(std::wstring dir)
{
	OBJECT_ATTRIBUTES objattr;
	HANDLE ret;
	UNICODE_STRING udir;
	_RtlInitUnicodeString(&udir, dir.c_str());
	InitializeObjectAttributes(&objattr, &udir, OBJ_CASE_INSENSITIVE, NULL, NULL);
	NTSTATUS status = _NtOpenDirectoryObject(&ret, MAXIMUM_ALLOWED, &objattr);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return NULL;
	}
	return ret;
}

HANDLE OpsMaster::OpenObjDir(std::string dir)
{
	return OpsMaster::OpenObjDir(std::wstring(dir.begin(), dir.end()));
}

bool OpsMaster::MakePermanentObj(HANDLE hobj)
{
	NTSTATUS status = _ZwMakePermanentObject(hobj);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::CreateAndWaitLock(std::wstring file, _UserCallback cb, bool IsDirectory)
{
	HANDLE h;
	if (IsDirectory)
		h = OpenDirectory(file);

	else
		h = OpenFileNative(file);
	if (h == INVALID_HANDLE_VALUE)
		return false;
	lock_ptr lk = FileOpLock::CreateLock(h, cb);
	if (lk != nullptr) { lk->WaitForLock(INFINITE); }
	else {
		_NtClose(h);
		delete lk;
		return false;
	}
	_NtClose(h);
	delete lk;
	return true;
}

bool OpsMaster::CreateAndWaitLock(std::string file, _UserCallback cb, bool IsDirectory)
{
	return OpsMaster::CreateAndWaitLock(std::wstring(file.begin(), file.end()), cb, IsDirectory);
}

bool OpsMaster::CreateAndWaitLock(HANDLE h, _UserCallback cb)
{
	FileOpLock* lk = FileOpLock::CreateLock(h, cb);
	if (lk != nullptr) {
		lk->WaitForLock(INFINITE);
		return false;
	}
	return true;
}

lock_ptr OpsMaster::CreateLock(HANDLE h, _UserCallback cb)
{
	lock_ptr lk = FileOpLock::CreateLock(h, cb);
	return lk;
}

lock_ptr OpsMaster::CreateLock(std::wstring file, _UserCallback cb, bool IsDirectory)
{
	HANDLE g;
	if (IsDirectory)
		g = OpsMaster::OpenDirectory(file);
	else
		g = OpsMaster::OpenFileNative(file);
	return OpsMaster::CreateLock(g, cb);
}

lock_ptr OpsMaster::CreateLock(std::string file, _UserCallback cb, bool IsDirectory)
{
	return OpsMaster::CreateLock(std::wstring(file.begin(), file.end()), cb, IsDirectory);
}

bool OpsMaster::MoveFileToTempDir(HANDLE h, DWORD temp_location, std::wstring loc)
{

	std::wstring randomstr = this->GenerateRandomStr();
	std::wstring path_to_move;
	WCHAR temp_path[MAX_PATH];
	switch (temp_location) {
	case USE_USER_TEMP_DIR:
		ExpandEnvironmentStringsW(L"%TEMP%", temp_path, MAX_PATH);
		path_to_move = temp_path + std::wstring(L"\\") + randomstr;
		return MoveByHandle(h, path_to_move);
		break;
	case USE_SYSTEM_TEMP_DIR:
		GetWindowsDirectoryW(temp_path, MAX_PATH);
		path_to_move = temp_path + std::wstring(L"\\Temp\\") + randomstr;
		return MoveByHandle(h, path_to_move);
		break;
	case USE_CUSTOM_TEMP_DIR:
		path_to_move = loc + std::wstring(L"\\") + randomstr;
		return MoveByHandle(h, path_to_move);
		break;
	}
	return false;
}

bool OpsMaster::MoveFileToTempDir(std::wstring file, bool IsDirectory, DWORD temp_location, std::wstring loc)
{
	HANDLE g;
	if (IsDirectory)
		g = OpsMaster::OpenDirectory(file, DELETE, ALL_SHARING, OPEN_EXISTING);
	else
		g = OpsMaster::OpenFileNative(file, DELETE, ALL_SHARING, OPEN_EXISTING);
	return OpsMaster::MoveFileToTempDir(g, temp_location, L"");
}

bool OpsMaster::MoveFileToTempDir(std::string file, bool IsDirectory, DWORD temp_location, std::string loc)
{
	return OpsMaster::MoveFileToTempDir(std::wstring(file.begin(), file.end()), IsDirectory, temp_location,
		std::wstring(loc.begin(), loc.end()));
}

bool OpsMaster::DeleteChild(HANDLE root, std::wstring child)
{
	OBJECT_ATTRIBUTES objattr;
	UNICODE_STRING _child;
	_RtlInitUnicodeString(&_child, child.c_str());
	InitializeObjectAttributes
	(
		&objattr,//object attributes pointer
		&_child,//object name in this case the file to be deleted
		OBJ_CASE_INSENSITIVE,//object attributes
		root,//root directory HANDLE
		NULL//security descriptor must be null
	);
	NTSTATUS status = _ZwDeleteFile(&objattr);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::DeleteFileNative(std::wstring full_path) {
	full_path = BuildNativePath(full_path);
	OBJECT_ATTRIBUTES objattr;
	UNICODE_STRING _full_path;
	_RtlInitUnicodeString(&_full_path, full_path.c_str());
	InitializeObjectAttributes
	(
		&objattr,
		&_full_path,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL
	);
	NTSTATUS status = _ZwDeleteFile(&objattr);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}
bool OpsMaster::DeleteFileNative(std::string full_path) {
	return OpsMaster::DeleteFileNative(std::wstring(full_path.begin(), full_path.end()));
}

bool OpsMaster::RRemoveDirectory(std::wstring dir)
{
	//this code isn't designed to handle symbolic link, only junctions
	DWORD fst_attr = GetFileAttributesW(dir.c_str());
	if (fst_attr & FILE_ATTRIBUTE_NORMAL)
		return OpsMaster::DeleteFileNative(dir);
	if (fst_attr & FILE_ATTRIBUTE_REPARSE_POINT)
		return RemoveDirectoryW(dir.c_str());
	std::wstring search_path = std::wstring(dir) + L"\\*.*";
	std::wstring s_p = std::wstring(dir) + std::wstring(L"\\");
	WIN32_FIND_DATAW fd;
	HANDLE hFind = FindFirstFileW(search_path.c_str(), &fd);
	if (hFind != INVALID_HANDLE_VALUE) {
		do {
			if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0)
			{
				continue;
			}
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
				RemoveDirectoryW(std::wstring(s_p + fd.cFileName).c_str());
				continue;
			}
			if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
				OpsMaster::DeleteFileNative(std::wstring(s_p + fd.cFileName));
				continue;
			}
			if (wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0)
			{
				OpsMaster::RRemoveDirectory(s_p + fd.cFileName);
			}
		} while (FindNextFileW(hFind, &fd));
		FindClose(hFind);
	}
	if (RemoveDirectoryW(dir.c_str()) != 0) {
		SetLastErr(GetLastErr());
		return false;
	}
	return true;
}


bool OpsMaster::RRemoveDirectory(std::string dir)
{
	return OpsMaster::RRemoveDirectory(std::wstring(dir.begin(), dir.end()));
}

bool OpsMaster::DeleteByHandle(HANDLE hfile)
{
	FILE_DISPOSITION_INFORMATION_EX dispositioninfo = { 0 };
	dispositioninfo.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE | FILE_DISPOSITION_POSIX_SEMANTICS;
	IO_STATUS_BLOCK io;
	NTSTATUS status = _NtSetInformationFile(hfile, &io, &dispositioninfo, sizeof(FILE_DISPOSITION_INFORMATION_EX), FileDispositionInformationEx);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}


std::wstring OpsMaster::GenerateRandomStr()
{
	GUID gg;
	HRESULT hs = CoCreateGuid(&gg);
	WCHAR mx[MAX_PATH];
	int x = StringFromGUID2(gg, mx, MAX_PATH);
	return mx;
}




OpsMaster::FileOpLock::FileOpLock(UserCallback cb) :
	g_inputBuffer({ 0 }), g_outputBuffer({ 0 }), g_o({ 0 }), g_hFile(INVALID_HANDLE_VALUE), g_hLockCompleted(nullptr), g_wait(nullptr), _cb(cb)
{
	g_inputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
	g_inputBuffer.StructureLength = sizeof(g_inputBuffer);
	g_inputBuffer.RequestedOplockLevel = OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE;
	g_inputBuffer.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
	g_outputBuffer.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
	g_outputBuffer.StructureLength = sizeof(g_outputBuffer);
}


OpsMaster::FileOpLock::~FileOpLock()
{
	if (g_wait)
	{
		SetThreadpoolWait(g_wait, nullptr, nullptr);
		CloseThreadpoolWait(g_wait);
		g_wait = nullptr;
	}

	if (g_o.hEvent)
	{
		CloseHandle(g_o.hEvent);
		g_o.hEvent = nullptr;
	}

	if (g_hFile != INVALID_HANDLE_VALUE)
	{
		CloseHandle(g_hFile);
		g_hFile = INVALID_HANDLE_VALUE;
	}
}

bool OpsMaster::FileOpLock::BeginLock(HANDLE h)
{
	g_hLockCompleted = CreateEvent(nullptr, TRUE, FALSE, nullptr);
	g_o.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);

	g_hFile = h;
	g_wait = CreateThreadpoolWait(WaitCallback, this, nullptr);
	if (g_wait == nullptr)
	{
		return false;
	}

	SetThreadpoolWait(g_wait, g_o.hEvent, nullptr);

	DeviceIoControl(g_hFile, FSCTL_REQUEST_OPLOCK,
		&g_inputBuffer, sizeof(g_inputBuffer),
		&g_outputBuffer, sizeof(g_outputBuffer),
		nullptr, &g_o);

	SetLastErr(GetLastError());
	if (GetLastError() != ERROR_IO_PENDING) {
		return false;
	}
	return true;
}

OpsMaster::FileOpLock* OpsMaster::FileOpLock::CreateLock(HANDLE h, OpsMaster::FileOpLock::UserCallback cb)
{
	OpsMaster::FileOpLock* ret = new OpsMaster::FileOpLock(cb);
	if (ret->BeginLock(h))
	{
		return ret;
	}
	else
	{
		delete ret;
		return nullptr;
	}
}

void OpsMaster::FileOpLock::WaitForLock(UINT Timeout)
{
	WaitForSingleObject(g_hLockCompleted, Timeout);
}

void OpsMaster::FileOpLock::WaitCallback(PTP_CALLBACK_INSTANCE Instance,
	PVOID Parameter, PTP_WAIT Wait,
	TP_WAIT_RESULT WaitResult)
{
	UNREFERENCED_PARAMETER(Instance);
	UNREFERENCED_PARAMETER(Wait);
	UNREFERENCED_PARAMETER(WaitResult);

	OpsMaster::FileOpLock* lock = reinterpret_cast<FileOpLock*>(Parameter);

	lock->DoWaitCallback();
}

void OpsMaster::FileOpLock::DoWaitCallback()
{
	DWORD dwBytes;
	if (!GetOverlappedResult(g_hFile, &g_o, &dwBytes, TRUE)) {
	}

	if (_cb)
	{
		_cb();
	}

	CloseHandle(g_hFile);
	g_hFile = INVALID_HANDLE_VALUE;
	SetEvent(g_hLockCompleted);
}

std::wstring OpsMaster::GetUserSid(HANDLE htoken) {

	//I'm gonna change this later, but too lazy to actually do it. I guess I'll stick with this for a while
	//It does work so there will be a quite long time before I touch this again
	DWORD dwSize;

	GetTokenInformation(htoken, TokenUser, nullptr, 0, &dwSize);

	std::vector<BYTE> userbuffer(dwSize);

	GetTokenInformation(htoken, TokenUser, &userbuffer[0], dwSize, &dwSize);

	PTOKEN_USER user = reinterpret_cast<PTOKEN_USER>(&userbuffer[0]);

	LPWSTR lpUser;
	std::wstring ret = L"";

	if (ConvertSidToStringSidW(user->User.Sid, &lpUser))
	{
		ret = lpUser;
		LocalFree(lpUser);
	}

	return ret;
}

HANDLE OpsMaster::GetAnonymousToken()
{
	HANDLE htoken = NULL;
	HANDLE hret = NULL;
	ImpersonateAnonymousToken(GetCurrentThread());
	OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &htoken);
	//just in case
	DuplicateHandle(GetCurrentProcess(), htoken, GetCurrentProcess(),
		&hret, TOKEN_ALL_ACCESS, FALSE, DUPLICATE_CLOSE_SOURCE);
	return hret;
}

bool OpsMaster::MakeTemporaryObj(HANDLE hobj)
{
	NTSTATUS status = _ZwMakeTemporaryObject(hobj);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

std::wstring OpsMaster::GetCurrentExeDir()
{
	WCHAR mx[MAX_PATH];
	GetModuleFileNameW(GetModuleHandle(NULL), mx, MAX_PATH);
	std::wstring ret = mx;
	for (int i = ret.size(); i > 0; i--) {
		if (ret[i] != L'\\') {
			continue;
		}
		ret.erase(i, ret.size());
		break;
	}
	return ret;
}

std::wstring OpsMaster::GetCurrentExeDirWithFileAppended(std::wstring file)
{

	return OpsMaster::GetCurrentExeDir() + L"\\" + file;
}

std::wstring OpsMaster::RegPathToNative(LPCWSTR lpPath)
{
	std::wstring regpath = L"\\REGISTRY\\";

	// Already native rooted
	if (lpPath[0] == '\\')
	{
		return lpPath;
	}

	if (_wcsnicmp(lpPath, L"HKLM\\", 5) == 0)
	{
		return regpath + L"MACHINE\\" + &lpPath[5];
	}
	else if (_wcsnicmp(lpPath, L"HKEY_LOCAL_MACHINE\\", 19) == 0) {
		return regpath + L"MACHINE\\" + &lpPath[19];
	}
	else if (_wcsnicmp(lpPath, L"HKU\\", 4) == 0)
	{
		return regpath + L"USER\\" + &lpPath[4];
	}
	else if (_wcsnicmp(lpPath, L"HKEY_USERS\\", 11) == 0)
	{
		return regpath + L"USER\\" + &lpPath[11];
	}
	else if (_wcsnicmp(lpPath, L"HKCU\\", 5) == 0)
	{
		std::wstring ret = regpath + L"USER\\" + GetUserSid().c_str() + L"\\" + &lpPath[5];
		return ret;
	}
	else if (_wcsnicmp(lpPath, L"HKEY_CURRENT_USER\\", 18) == 0)
	{
		std::wstring ret = regpath + L"USER\\" + GetUserSid().c_str() + L"\\" + &lpPath[18];
		return ret;
	}
	else
	{
		//error
		return L"";
	}
}

HANDLE OpsMaster::RegCreateKeyNative(std::wstring target, DWORD desired_access, bool OpenLink, bool CreateLink)
{
	HANDLE hret = NULL;
	UNICODE_STRING _target;
	target = RegPathToNative(target.c_str());
	_RtlInitUnicodeString(&_target, target.c_str());
	DWORD _objflag = OBJ_CASE_INSENSITIVE | (OpenLink ? OBJ_OPENLINK : NULL);
	OBJECT_ATTRIBUTES objattr;
	InitializeObjectAttributes(&objattr, &_target, _objflag, NULL, NULL);
	NTSTATUS status = _ZwCreateKey(&hret, desired_access,
		&objattr, NULL, NULL,
		(CreateLink ? INTERNAL_REG_OPTION_CREATE_LINK : NULL) | REG_OPTION_NON_VOLATILE, NULL);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
	}
	return hret;
}

HANDLE OpsMaster::RegCreateKeyNative(std::string target, DWORD desired_access, bool OpenLink, bool CreateLink) {
	return OpsMaster::RegCreateKeyNative(
		std::wstring(target.begin(), target.end()),
		desired_access, OpenLink, CreateLink
	);
}

bool OpsMaster::RegDeleteKeyNative(HANDLE hkey) {

	NTSTATUS status = _ZwDeleteKey(hkey);
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;
}

bool OpsMaster::RegDeleteKeyNative(std::wstring target) {
	HANDLE hk = OpsMaster::RegCreateKeyNative(target, DELETE, true);
	bool ret = OpsMaster::RegDeleteKeyNative(hk);
	_NtClose(hk);
	return ret;
}

bool OpsMaster::RegDeleteKeyNative(std::string target) {
	return OpsMaster::RegDeleteKeyNative(std::wstring(target.begin(), target.end()));
}

bool OpsMaster::RegCreateNativeLink(HANDLE hkey, std::wstring target) {

	UNICODE_STRING value;
	target = RegPathToNative(target.c_str());
	_RtlInitUnicodeString(&value, L"SymbolicLinkValue");

	NTSTATUS status = _ZwSetValueKey(hkey, &value, NULL, REG_LINK,
		(PVOID)target.c_str(), target.length() * sizeof(WCHAR));
	if (status != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(status));
		return false;
	}
	return true;

}
bool OpsMaster::RegCreateNativeLink(HANDLE hkey, std::string target) {
	return OpsMaster::RegCreateNativeLink(hkey, std::wstring(target.begin(), target.end()));
}
bool OpsMaster::RegCreateNativeLink(std::wstring link, std::wstring target) {
	HANDLE hk = OpsMaster::RegCreateKeyNative(link, KEY_CREATE_LINK | KEY_WRITE, true, true);
	if (!hk) {
		return false;
	}
	bool ret = OpsMaster::RegCreateNativeLink(hk, target);
	_NtClose(hk);
	return ret;
}
bool OpsMaster::RegCreateNativeLink(std::string link, std::string target) {
	std::wstring lnk = std::wstring(link.begin(), link.end());
	std::wstring _target = std::wstring(target.begin(), target.end());
	return OpsMaster::RegCreateNativeLink(lnk,
		_target);
}

bool OpsMaster::SetShortFileNameNative(HANDLE hfile, std::wstring short_name) {

	size_t fni_sz = sizeof(FILE_NAME_INFORMATION) + (short_name.size() * sizeof(WCHAR));
	FILE_NAME_INFORMATION* fni = (FILE_NAME_INFORMATION*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, fni_sz);
	fni->FileNameLength = short_name.size() * sizeof(WCHAR);
	memcpy(&fni->FileName[0], short_name.c_str(), fni->FileNameLength);
	IO_STATUS_BLOCK io_status = { 0 };
	NTSTATUS stat = _NtSetInformationFile(hfile, &io_status, fni, fni_sz, FileShortNameInformation);
	HeapFree(GetProcessHeap(), NULL, fni);
	if (stat != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(stat));
		return false;
	}
	return true;

}

bool OpsMaster::SetCaseSensitiveDirectory(HANDLE hf, bool enable) {
	;
	FILE_CASE_SENSITIVE_INFORMATION fcsi = { FILE_CS_FLAG_CASE_SENSITIVE_DIR };
	IO_STATUS_BLOCK io = { 0 };
	NTSTATUS stat = _NtSetInformationFile(hf, &io, &fcsi, sizeof(fcsi), FileCaseSensitiveInformation);
	if (stat != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(stat));
		return false;
	}
	return true;
}

/*
HANDLE OpsMaster::OpenObjLink(std::wstring link,DWORD desired_access) {

	UNICODE_STRING lnk;
	_RtlInitUnicodeString(&lnk, link.c_str());
	OBJECT_ATTRIBUTES objattr;
	InitializeObjectAttributes(&objattr, &lnk, OBJ_CASE_INSENSITIVE, NULL, NULL);

	HANDLE hd = NULL;
	NTSTATUS stat = _NtOpenSymbolicLinkObject(&hd, desired_access, &objattr);
	if (stat != STATUS_SUCCESS) {
		SetLastErr(_RtlNtStatusToDosError(stat));
		return NULL;
	}
	return hd;
}
//not tested yet
*/