#include "ntdll.h" #include "common.h" #include "context.h" #include "errors.h" #include "files.h" #include "handles.h" #include "heap.h" #include "kernel32/fileapi.h" #include "kernel32/internal.h" #include "kernel32/minwinbase.h" #include "kernel32/processthreadsapi.h" #include "modules.h" #include "processes.h" #include "strutil.h" #include "types.h" #include #include #include #include #include #include #include namespace { struct PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; GUEST_PTR PebBaseAddress; ULONG_PTR AffinityMask; LONG BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; }; struct ProcessHandleDetails { pid_t pid = -1; DWORD exitCode = STILL_ACTIVE; PEB *peb = nullptr; bool isCurrentProcess = false; }; constexpr LONG kDefaultBasePriority = 8; struct RTL_OSVERSIONINFOEXW : RTL_OSVERSIONINFOW { WORD wServicePackMajor; WORD wServicePackMinor; WORD wSuiteMask; BYTE wProductType; BYTE wReserved; }; using PRTL_OSVERSIONINFOEXW = RTL_OSVERSIONINFOEXW *; constexpr ULONG kOsMajorVersion = 6; constexpr ULONG kOsMinorVersion = 2; constexpr ULONG kOsBuildNumber = 0; constexpr ULONG kOsPlatformId = 2; // VER_PLATFORM_WIN32_NT constexpr BYTE kProductTypeWorkstation = 1; // VER_NT_WORKSTATION constexpr ULONGLONG kHundredNanosecondsPerSecond = 10'000'000ULL; constexpr ULONGLONG kUnixEpochAsFileTime = 116'444'736'000'000'000ULL; struct StatFetchResult { bool ok = false; int err = 0; }; #if defined(__APPLE__) timespec accessTimespec(const struct stat &st) { return st.st_atimespec; } timespec modifyTimespec(const struct stat &st) { return st.st_mtimespec; } timespec changeTimespec(const struct stat &st) { return st.st_ctimespec; } #elif defined(__linux__) timespec accessTimespec(const struct stat &st) { return st.st_atim; } timespec modifyTimespec(const struct stat &st) { return st.st_mtim; } timespec changeTimespec(const struct stat &st) { return st.st_ctim; } #else timespec accessTimespec(const struct stat &st) { return timespec{.tv_sec = st.st_atime, .tv_nsec = 0}; } timespec modifyTimespec(const struct stat &st) { return timespec{.tv_sec = st.st_mtime, .tv_nsec = 0}; } timespec changeTimespec(const struct stat &st) { return timespec{.tv_sec = st.st_ctime, .tv_nsec = 0}; } #endif LONGLONG timespecToFileTime(const timespec &ts) { #if defined(__SIZEOF_INT128__) __int128 ticks = static_cast<__int128>(ts.tv_sec) * static_cast<__int128>(kHundredNanosecondsPerSecond); ticks += static_cast<__int128>(ts.tv_nsec / 100); ticks += static_cast<__int128>(kUnixEpochAsFileTime); if (ticks < 0) { return 0; } if (ticks > static_cast<__int128>(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(ticks); #else long double ticks = static_cast(ts.tv_sec) * static_cast(kHundredNanosecondsPerSecond); ticks += static_cast(ts.tv_nsec) / 100.0L; ticks += static_cast(kUnixEpochAsFileTime); if (ticks < 0.0L) { return 0; } if (ticks > static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(ticks); #endif } DWORD buildFileAttributes(const struct stat &st) { DWORD attributes = 0; mode_t mode = st.st_mode; if (S_ISDIR(mode)) { attributes |= FILE_ATTRIBUTE_DIRECTORY; } if (S_ISREG(mode)) { attributes |= FILE_ATTRIBUTE_ARCHIVE; } if ((mode & S_IWUSR) == 0) { attributes |= FILE_ATTRIBUTE_READONLY; } if (attributes == 0) { attributes = FILE_ATTRIBUTE_NORMAL; } return attributes; } StatFetchResult fetchStat(kernel32::FsObject *fs, struct stat &st) { if (!fs) { return {}; } if (fs->valid()) { if (fstat(fs->fd, &st) == 0) { return StatFetchResult{.ok = true, .err = 0}; } if (errno != EBADF) { return StatFetchResult{.ok = false, .err = errno}; } } if (!fs->canonicalPath.empty()) { if (stat(fs->canonicalPath.c_str(), &st) == 0) { return StatFetchResult{.ok = true, .err = 0}; } return StatFetchResult{.ok = false, .err = errno}; } return StatFetchResult{}; } bool resolveProcessDetails(HANDLE processHandle, ProcessHandleDetails &details) { if (kernel32::isPseudoCurrentProcessHandle(processHandle)) { details.pid = getpid(); details.exitCode = STILL_ACTIVE; details.peb = wibo::processPeb; details.isCurrentProcess = true; return true; } auto po = wibo::handles().getAs(processHandle); if (!po) { return false; } details.pid = po->pid; details.exitCode = po->exitCode; details.isCurrentProcess = po->pid == getpid(); details.peb = details.isCurrentProcess ? wibo::processPeb : nullptr; return true; } std::string windowsImagePathFor(const ProcessHandleDetails &details) { if (details.isCurrentProcess && !wibo::guestExecutablePath.empty()) { return files::pathToWindows(files::canonicalPath(wibo::guestExecutablePath)); } std::error_code ec; std::filesystem::path link = std::filesystem::path("/proc") / std::to_string(details.pid) / "exe"; std::filesystem::path resolved = std::filesystem::read_symlink(link, ec); if (!ec) { return files::pathToWindows(files::canonicalPath(resolved)); } return {}; } } // namespace namespace kernel32 { BOOL WINAPI SetEvent(HANDLE hEvent); BOOL WINAPI ResetEvent(HANDLE hEvent); } // namespace kernel32 namespace ntdll { constexpr LARGE_INTEGER FILE_WRITE_TO_END_OF_FILE = {.QuadPart = -1}; constexpr LARGE_INTEGER FILE_USE_FILE_POINTER_POSITION = {.QuadPart = -2}; PVOID CDECL memset(PVOID dest, int ch, SIZE_T count) { VERBOSE_LOG("ntdll::memset(%p, %i, %zu)\n", dest, ch, count); return std::memset(dest, ch, count); } NTSTATUS WINAPI NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtReadFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key); (void)ApcRoutine; (void)ApcContext; (void)Key; if (!IoStatusBlock) { return STATUS_INVALID_PARAMETER; } IoStatusBlock->Information = 0; auto file = wibo::handles().getAs(FileHandle); if (!file || !file->valid()) { IoStatusBlock->Status = STATUS_INVALID_HANDLE; IoStatusBlock->Information = 0; return STATUS_INVALID_HANDLE; } bool useOverlapped = file->overlapped; bool useCurrentFilePosition = (ByteOffset == nullptr); if (!useCurrentFilePosition && ByteOffset->QuadPart == FILE_USE_FILE_POINTER_POSITION.QuadPart) { useCurrentFilePosition = true; } std::optional offset; if (!useCurrentFilePosition) { offset = static_cast(ByteOffset->QuadPart); } if (useOverlapped && useCurrentFilePosition) { IoStatusBlock->Status = STATUS_INVALID_PARAMETER; IoStatusBlock->Information = 0; return STATUS_INVALID_PARAMETER; } Pin ev; if (Event) { ev = wibo::handles().getAs(Event); if (!ev) { IoStatusBlock->Status = STATUS_INVALID_HANDLE; IoStatusBlock->Information = 0; return STATUS_INVALID_HANDLE; } ev->reset(); } bool updateFilePointer = !useOverlapped; auto io = files::read(file.get(), Buffer, Length, offset, updateFilePointer); NTSTATUS status = STATUS_SUCCESS; if (io.unixError != 0) { status = wibo::statusFromErrno(io.unixError); } else if (io.reachedEnd && io.bytesTransferred == 0) { status = file->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE; } IoStatusBlock->Status = status; IoStatusBlock->Information = static_cast(io.bytesTransferred); if (ev && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE)) { ev->set(); } DEBUG_LOG("-> 0x%x\n", status); return status; } NTSTATUS WINAPI NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtWriteFile(%p, %p, %p, %p, %p, %p, %u, %p, %p) ", FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key); (void)ApcRoutine; (void)ApcContext; (void)Key; if (!IoStatusBlock) { return STATUS_INVALID_PARAMETER; } IoStatusBlock->Information = 0; auto file = wibo::handles().getAs(FileHandle); if (!file || !file->valid()) { IoStatusBlock->Status = STATUS_INVALID_HANDLE; return STATUS_INVALID_HANDLE; } bool useOverlapped = file->overlapped; bool useCurrentFilePosition = (ByteOffset == nullptr); bool writeToEndOfFile = false; if (ByteOffset) { if (ByteOffset->QuadPart == FILE_USE_FILE_POINTER_POSITION.QuadPart) { useCurrentFilePosition = true; } else if (ByteOffset->QuadPart == FILE_WRITE_TO_END_OF_FILE.QuadPart) { writeToEndOfFile = true; } } std::optional offset; if (!useCurrentFilePosition && !writeToEndOfFile) { offset = static_cast(ByteOffset->QuadPart); } if (useOverlapped && useCurrentFilePosition) { IoStatusBlock->Status = STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER; } Pin ev; if (Event) { ev = wibo::handles().getAs(Event); if (!ev) { IoStatusBlock->Status = STATUS_INVALID_HANDLE; return STATUS_INVALID_HANDLE; } ev->reset(); } bool updateFilePointer = file->isPipe ? true : !useOverlapped; if (writeToEndOfFile && !offset.has_value()) { if (!file->isPipe) { struct stat st{}; if (fstat(file->fd, &st) != 0) { int err = errno ? errno : EIO; NTSTATUS status = wibo::statusFromErrno(err); IoStatusBlock->Status = status; return status; } offset = static_cast(st.st_size); } } auto io = files::write(file.get(), Buffer, static_cast(Length), offset, updateFilePointer); NTSTATUS status = STATUS_SUCCESS; if (io.unixError != 0) { status = wibo::statusFromErrno(io.unixError); } IoStatusBlock->Status = status; IoStatusBlock->Information = static_cast(io.bytesTransferred); if (ev && status == STATUS_SUCCESS) { ev->set(); } DEBUG_LOG("-> 0x%x\n", status); return status; } NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtAllocateVirtualMemory(%p, %p, %lu, %p, %lu, %lu) ", ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocationType, Protect); if (ProcessHandle != (HANDLE)-1) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_HANDLE); return STATUS_INVALID_HANDLE; } if (ZeroBits != 0 || BaseAddress == nullptr || RegionSize == nullptr) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } void *baseAddress = BaseAddress->get(); size_t regionSize = static_cast(*RegionSize); wibo::heap::VmStatus vmStatus = wibo::heap::virtualAlloc( &baseAddress, ®ionSize, static_cast(AllocationType), static_cast(Protect)); if (vmStatus != wibo::heap::VmStatus::Success) { NTSTATUS status = wibo::heap::ntStatusFromVmStatus(vmStatus); DEBUG_LOG("-> 0x%x\n", status); return status; } *BaseAddress = baseAddress; *RegionSize = static_cast(regionSize); DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, PSIZE_T NumberOfBytesToProtect, ULONG NewAccessProtection, PULONG OldAccessProtection) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtProtectVirtualMemory(%p, %p, %p, %lu, %p) ", ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection); if (ProcessHandle != (HANDLE)-1) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_HANDLE); return STATUS_INVALID_HANDLE; } if (BaseAddress == nullptr || NumberOfBytesToProtect == nullptr) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } void *base = BaseAddress->get(); size_t length = static_cast(*NumberOfBytesToProtect); wibo::heap::VmStatus vmStatus = wibo::heap::virtualProtect(base, length, static_cast(NewAccessProtection), OldAccessProtection); if (vmStatus != wibo::heap::VmStatus::Success) { NTSTATUS status = wibo::heap::ntStatusFromVmStatus(vmStatus); DEBUG_LOG("-> 0x%x\n", status); return status; } DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } NTSTATUS WINAPI NtQueryInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtQueryInformationFile(%p, %p, %p, %u, %u) ", FileHandle, IoStatusBlock, FileInformation, Length, static_cast(FileInformationClass)); if (!IoStatusBlock) { DEBUG_LOG("-> 0x%x\n", STATUS_ACCESS_VIOLATION); return STATUS_ACCESS_VIOLATION; } IoStatusBlock->Information = 0; if (Length != 0 && !FileInformation) { IoStatusBlock->Status = STATUS_ACCESS_VIOLATION; DEBUG_LOG("-> 0x%x\n", STATUS_ACCESS_VIOLATION); return STATUS_ACCESS_VIOLATION; } if (reinterpret_cast(FileHandle) < 0) { IoStatusBlock->Status = STATUS_OBJECT_TYPE_MISMATCH; DEBUG_LOG("-> 0x%x\n", STATUS_OBJECT_TYPE_MISMATCH); return STATUS_OBJECT_TYPE_MISMATCH; } auto obj = wibo::handles().getAs(FileHandle); if (!obj || !obj->valid()) { IoStatusBlock->Status = STATUS_INVALID_HANDLE; DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_HANDLE); return STATUS_INVALID_HANDLE; } std::lock_guard lock(obj->m); NTSTATUS status = STATUS_SUCCESS; switch (FileInformationClass) { case FileBasicInformation: { if (Length < sizeof(FILE_BASIC_INFORMATION)) { status = STATUS_INFO_LENGTH_MISMATCH; break; } struct stat st{}; StatFetchResult statRes = fetchStat(obj.get(), st); if (!statRes.ok) { status = wibo::statusFromErrno(statRes.err != 0 ? statRes.err : EINVAL); break; } auto info = reinterpret_cast(FileInformation); info->CreationTime.QuadPart = timespecToFileTime(changeTimespec(st)); info->LastAccessTime.QuadPart = timespecToFileTime(accessTimespec(st)); info->LastWriteTime.QuadPart = timespecToFileTime(modifyTimespec(st)); info->ChangeTime.QuadPart = timespecToFileTime(changeTimespec(st)); info->FileAttributes = buildFileAttributes(st); IoStatusBlock->Information = sizeof(FILE_BASIC_INFORMATION); break; } case FileStandardInformation: { if (Length < sizeof(FILE_STANDARD_INFORMATION)) { status = STATUS_INFO_LENGTH_MISMATCH; break; } struct stat st{}; StatFetchResult statRes = fetchStat(obj.get(), st); if (!statRes.ok) { status = wibo::statusFromErrno(statRes.err != 0 ? statRes.err : EINVAL); break; } auto info = reinterpret_cast(FileInformation); unsigned long long allocation = static_cast(st.st_blocks) * 512ULL; info->AllocationSize.QuadPart = static_cast(allocation); info->EndOfFile.QuadPart = static_cast(st.st_size); info->NumberOfLinks = static_cast(st.st_nlink); info->DeletePending = obj->deletePending ? TRUE : FALSE; info->Directory = S_ISDIR(st.st_mode) ? TRUE : FALSE; info->Reserved = 0; IoStatusBlock->Information = sizeof(FILE_STANDARD_INFORMATION); break; } case FilePositionInformation: { auto file = std::move(obj).downcast(); if (!file) { status = STATUS_INVALID_PARAMETER; break; } if (Length < sizeof(FILE_POSITION_INFORMATION)) { status = STATUS_INFO_LENGTH_MISMATCH; break; } auto info = reinterpret_cast(FileInformation); info->CurrentByteOffset.QuadPart = static_cast(file->filePos); IoStatusBlock->Information = sizeof(FILE_POSITION_INFORMATION); break; } case FileNameInformation: { if (Length < sizeof(ULONG)) { status = STATUS_INFO_LENGTH_MISMATCH; break; } std::string windowsPath; if (!obj->canonicalPath.empty()) { windowsPath = files::pathToWindows(obj->canonicalPath); } std::string volumeRelative; if (!windowsPath.empty()) { if (windowsPath.size() >= 2 && windowsPath[1] == ':') { volumeRelative = windowsPath.substr(2); if (volumeRelative.empty() || volumeRelative.front() != '\\') { volumeRelative.insert(volumeRelative.begin(), '\\'); } } else if (!windowsPath.empty() && windowsPath.front() != '\\') { volumeRelative = "\\" + windowsPath; } else { volumeRelative = windowsPath; } } auto info = reinterpret_cast(FileInformation); auto wide = stringToWideString(volumeRelative.c_str(), volumeRelative.size()); size_t charCount = wide.empty() ? 0 : wstrlen(wide.data()); size_t bytesRequired = charCount * sizeof(uint16_t); if (Length < sizeof(ULONG) + bytesRequired) { info->FileNameLength = static_cast(bytesRequired); status = STATUS_INFO_LENGTH_MISMATCH; break; } info->FileNameLength = static_cast(bytesRequired); if (bytesRequired > 0) { std::memcpy(info->FileName, wide.data(), bytesRequired); } IoStatusBlock->Information = static_cast(sizeof(ULONG) + bytesRequired); break; } default: DEBUG_LOG("FIXME: NtQueryInformationFile: Unsupported info class"); status = STATUS_INVALID_INFO_CLASS; break; } IoStatusBlock->Status = status; DEBUG_LOG("-> 0x%x\n", status); return status; } NTSTATUS WINAPI NtQuerySystemTime(PLARGE_INTEGER SystemTime) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtQuerySystemTime(%p) ", SystemTime); if (!SystemTime) { DEBUG_LOG("-> 0x%x\n", STATUS_ACCESS_VIOLATION); return STATUS_ACCESS_VIOLATION; } using HundredNanoseconds = std::chrono::duration>; auto now = std::chrono::system_clock::now().time_since_epoch(); auto sinceUnix = std::chrono::duration_cast(now).count(); ULONGLONG fileTime = kUnixEpochAsFileTime + static_cast(sinceUnix); SystemTime->QuadPart = static_cast(fileTime); DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } BOOLEAN WINAPI RtlTimeToSecondsSince1970(PLARGE_INTEGER Time, PULONG ElapsedSeconds) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlTimeToSecondsSince1970(%p, %p) ", Time, ElapsedSeconds); if (!Time || !ElapsedSeconds) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } LONGLONG fileTimeSigned = Time->QuadPart; if (fileTimeSigned < 0) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } ULONGLONG fileTime = static_cast(fileTimeSigned); if (fileTime < kUnixEpochAsFileTime) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } ULONGLONG delta = fileTime - kUnixEpochAsFileTime; ULONGLONG seconds = delta / kHundredNanosecondsPerSecond; if (seconds > 0xFFFFFFFFULL) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } *ElapsedSeconds = static_cast(seconds); DEBUG_LOG("-> %u\n", TRUE); return TRUE; } VOID WINAPI RtlInitializeBitMap(PRTL_BITMAP BitMapHeader, PULONG BitMapBuffer, ULONG SizeOfBitMap) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlInitializeBitMap(%p, %p, %u)\n", BitMapHeader, BitMapBuffer, SizeOfBitMap); if (!BitMapHeader) { return; } BitMapHeader->SizeOfBitMap = SizeOfBitMap; BitMapHeader->Buffer = BitMapBuffer; } VOID WINAPI RtlSetBits(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG NumberToSet) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlSetBits(%p, %u, %u)\n", BitMapHeader, StartingIndex, NumberToSet); if (!BitMapHeader || !BitMapHeader->Buffer || NumberToSet == 0) { return; } ULONG size = BitMapHeader->SizeOfBitMap; if (StartingIndex >= size) { return; } ULONG available = size - StartingIndex; if (NumberToSet > available) { NumberToSet = available; } for (ULONG i = 0; i < NumberToSet; ++i) { ULONG bitIndex = StartingIndex + i; ULONG wordIndex = bitIndex / 32; ULONG offset = bitIndex % 32; BitMapHeader->Buffer[wordIndex] |= (1u << offset); } } BOOLEAN WINAPI RtlAreBitsSet(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlAreBitsSet(%p, %u, %u) ", BitMapHeader, StartingIndex, Length); if (!BitMapHeader || !BitMapHeader->Buffer || Length == 0) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } ULONG size = BitMapHeader->SizeOfBitMap; if (StartingIndex >= size || Length > size - StartingIndex) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } for (ULONG i = 0; i < Length; ++i) { ULONG bitIndex = StartingIndex + i; ULONG wordIndex = bitIndex / 32; ULONG offset = bitIndex % 32; if ((BitMapHeader->Buffer[wordIndex] & (1u << offset)) == 0) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } } DEBUG_LOG("-> %u\n", TRUE); return TRUE; } BOOLEAN WINAPI RtlAreBitsClear(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlAreBitsClear(%p, %u, %u) ", BitMapHeader, StartingIndex, Length); if (!BitMapHeader || !BitMapHeader->Buffer || Length == 0) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } ULONG size = BitMapHeader->SizeOfBitMap; if (StartingIndex >= size || Length > size - StartingIndex) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } for (ULONG i = 0; i < Length; ++i) { ULONG bitIndex = StartingIndex + i; ULONG wordIndex = bitIndex / 32; ULONG offset = bitIndex % 32; if ((BitMapHeader->Buffer[wordIndex] & (1u << offset)) != 0) { DEBUG_LOG("-> %u\n", FALSE); return FALSE; } } DEBUG_LOG("-> %u\n", TRUE); return TRUE; } NTSTATUS WINAPI RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation) { HOST_CONTEXT_GUARD(); DEBUG_LOG("RtlGetVersion(%p) ", lpVersionInformation); if (!lpVersionInformation) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } ULONG size = lpVersionInformation->dwOSVersionInfoSize; if (size < sizeof(RTL_OSVERSIONINFOW)) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } std::memset(lpVersionInformation, 0, static_cast(size)); lpVersionInformation->dwOSVersionInfoSize = size; lpVersionInformation->dwMajorVersion = kOsMajorVersion; lpVersionInformation->dwMinorVersion = kOsMinorVersion; lpVersionInformation->dwBuildNumber = kOsBuildNumber; lpVersionInformation->dwPlatformId = kOsPlatformId; if (size >= sizeof(RTL_OSVERSIONINFOEXW)) { auto extended = reinterpret_cast(lpVersionInformation); extended->wProductType = kProductTypeWorkstation; } DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } NTSTATUS WINAPI NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength) { HOST_CONTEXT_GUARD(); DEBUG_LOG("NtQueryInformationProcess(%d, %u, %p, %u, %p) ", ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); if (!ProcessInformation) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_PARAMETER); return STATUS_INVALID_PARAMETER; } ProcessHandleDetails details{}; if (!resolveProcessDetails(ProcessHandle, details)) { DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_HANDLE); return STATUS_INVALID_HANDLE; } switch (ProcessInformationClass) { case ProcessBasicInformation: { size_t required = sizeof(PROCESS_BASIC_INFORMATION); if (ReturnLength) { *ReturnLength = static_cast(required); } if (ProcessInformationLength < required) { DEBUG_LOG("-> 0x%x\n", STATUS_INFO_LENGTH_MISMATCH); return STATUS_INFO_LENGTH_MISMATCH; } auto *info = reinterpret_cast(ProcessInformation); std::memset(info, 0, sizeof(*info)); info->ExitStatus = static_cast(details.exitCode); info->PebBaseAddress = toGuestPtr(details.peb); DWORD_PTR processMask = 0; DWORD_PTR systemMask = 0; if (kernel32::GetProcessAffinityMask(ProcessHandle, &processMask, &systemMask)) { info->AffinityMask = static_cast(processMask == 0 ? 1 : processMask); } else { info->AffinityMask = 1; } info->BasePriority = kDefaultBasePriority; info->UniqueProcessId = static_cast(details.pid); if (details.isCurrentProcess) { info->InheritedFromUniqueProcessId = static_cast(getppid()); } else { info->InheritedFromUniqueProcessId = static_cast(getpid()); } DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } case ProcessWow64Information: { size_t required = sizeof(ULONG_PTR); if (ReturnLength) { *ReturnLength = static_cast(required); } if (ProcessInformationLength < required) { DEBUG_LOG("-> 0x%x\n", STATUS_INFO_LENGTH_MISMATCH); return STATUS_INFO_LENGTH_MISMATCH; } auto *value = reinterpret_cast(ProcessInformation); *value = 0; DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } case ProcessImageFileName: { size_t minimum = sizeof(UNICODE_STRING); if (ProcessInformationLength < minimum) { if (ReturnLength) { *ReturnLength = static_cast(minimum); } DEBUG_LOG("-> 0x%x\n", STATUS_INFO_LENGTH_MISMATCH); return STATUS_INFO_LENGTH_MISMATCH; } std::string imagePath = windowsImagePathFor(details); DEBUG_LOG(" NtQueryInformationProcess image path: %s\n", imagePath.c_str()); auto widePath = stringToWideString(imagePath.c_str()); size_t stringBytes = widePath.size() * sizeof(uint16_t); size_t required = sizeof(UNICODE_STRING) + stringBytes; if (ReturnLength) { *ReturnLength = static_cast(required); } if (ProcessInformationLength < required) { DEBUG_LOG("-> 0x%x\n", STATUS_INFO_LENGTH_MISMATCH); return STATUS_INFO_LENGTH_MISMATCH; } auto *unicode = reinterpret_cast(ProcessInformation); auto *buffer = reinterpret_cast(reinterpret_cast(ProcessInformation) + sizeof(UNICODE_STRING)); std::memcpy(buffer, widePath.data(), stringBytes); size_t characterCount = widePath.empty() ? 0 : widePath.size() - 1; unicode->Length = static_cast(characterCount * sizeof(uint16_t)); unicode->MaximumLength = static_cast(widePath.size() * sizeof(uint16_t)); unicode->Buffer = toGuestPtr(buffer); DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; } default: DEBUG_LOG("-> 0x%x\n", STATUS_INVALID_INFO_CLASS); return STATUS_INVALID_INFO_CLASS; } } NTSTATUS WINAPI LdrAddRefDll(ULONG Flags, HMODULE Module) { DEBUG_LOG("STUB: LdrAddRefDll(%x, %p)\n", Flags, Module); (void)Flags; (void)Module; return STATUS_SUCCESS; } } // namespace ntdll #include "ntdll_trampolines.h" extern const wibo::ModuleStub lib_ntdll = { (const char *[]){ "ntdll", nullptr, }, ntdllThunkByName, nullptr, };