mirror of
https://github.com/decompals/wibo.git
synced 2025-12-18 01:15:37 +00:00
More ntdll impls; fix 64-bit Clang assembly
This commit is contained in:
@@ -390,11 +390,14 @@ if (WIBO_ENABLE_FIXTURE_TESTS)
|
|||||||
wibo_add_fixture_bin(NAME test_actctx SOURCES test/test_actctx.c)
|
wibo_add_fixture_bin(NAME test_actctx SOURCES test/test_actctx.c)
|
||||||
wibo_add_fixture_bin(NAME test_overlapped_io SOURCES test/test_overlapped_io.c)
|
wibo_add_fixture_bin(NAME test_overlapped_io SOURCES test/test_overlapped_io.c)
|
||||||
wibo_add_fixture_bin(NAME test_time SOURCES test/test_time.c)
|
wibo_add_fixture_bin(NAME test_time SOURCES test/test_time.c)
|
||||||
|
wibo_add_fixture_bin(NAME test_ntdll_time SOURCES test/test_ntdll_time.c)
|
||||||
wibo_add_fixture_bin(NAME test_virtualalloc SOURCES test/test_virtualalloc.c)
|
wibo_add_fixture_bin(NAME test_virtualalloc SOURCES test/test_virtualalloc.c)
|
||||||
wibo_add_fixture_bin(NAME test_virtualquery SOURCES test/test_virtualquery.c)
|
wibo_add_fixture_bin(NAME test_virtualquery SOURCES test/test_virtualquery.c)
|
||||||
wibo_add_fixture_bin(NAME test_clsids SOURCES test/test_clsids.c COMPILE_OPTIONS -lole32)
|
wibo_add_fixture_bin(NAME test_clsids SOURCES test/test_clsids.c COMPILE_OPTIONS -lole32)
|
||||||
wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c)
|
wibo_add_fixture_bin(NAME test_rtl SOURCES test/test_rtl.c)
|
||||||
|
wibo_add_fixture_bin(NAME test_rtl_bitmap SOURCES test/test_rtl_bitmap.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
wibo_add_fixture_bin(NAME test_ntquery SOURCES test/test_ntquery.c)
|
||||||
|
wibo_add_fixture_bin(NAME test_ntqueryfile SOURCES test/test_ntqueryfile.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
wibo_add_fixture_bin(NAME test_ntreadfile SOURCES test/test_ntreadfile.c)
|
||||||
wibo_add_fixture_bin(NAME test_ntwritefile SOURCES test/test_ntwritefile.c)
|
wibo_add_fixture_bin(NAME test_ntwritefile SOURCES test/test_ntwritefile.c)
|
||||||
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
wibo_add_fixture_bin(NAME test_pipe_io SOURCES test/test_pipe_io.c)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ struct FsObject : ObjectBase {
|
|||||||
[[nodiscard]] bool valid() const { return fd >= 0; }
|
[[nodiscard]] bool valid() const { return fd >= 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit FsObject(ObjectType type, int fd) : ObjectBase(type), fd(fd) {}
|
explicit FsObject(ObjectType type, int fd) : ObjectBase(type), fd(fd) { flags |= Of_FsObject; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileObject : FsObject {
|
struct FileObject : FsObject {
|
||||||
@@ -163,13 +163,9 @@ struct HeapObject : public ObjectBase {
|
|||||||
inline constexpr HANDLE kPseudoCurrentProcessHandleValue = static_cast<HANDLE>(-1);
|
inline constexpr HANDLE kPseudoCurrentProcessHandleValue = static_cast<HANDLE>(-1);
|
||||||
inline constexpr HANDLE kPseudoCurrentThreadHandleValue = static_cast<HANDLE>(-2);
|
inline constexpr HANDLE kPseudoCurrentThreadHandleValue = static_cast<HANDLE>(-2);
|
||||||
|
|
||||||
inline bool isPseudoCurrentProcessHandle(HANDLE h) {
|
inline bool isPseudoCurrentProcessHandle(HANDLE h) { return h == kPseudoCurrentProcessHandleValue; }
|
||||||
return h == kPseudoCurrentProcessHandleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isPseudoCurrentThreadHandle(HANDLE h) {
|
inline bool isPseudoCurrentThreadHandle(HANDLE h) { return h == kPseudoCurrentThreadHandleValue; }
|
||||||
return h == kPseudoCurrentThreadHandleValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tryMarkExecutable(void *mem);
|
void tryMarkExecutable(void *mem);
|
||||||
void setLastErrorFromErrno();
|
void setLastErrorFromErrno();
|
||||||
@@ -182,6 +178,10 @@ void setLastError(DWORD error);
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
template <> constexpr bool typeMatches<kernel32::FsObject>(const ObjectBase *o) noexcept {
|
||||||
|
return o && (o->flags & Of_FsObject);
|
||||||
|
}
|
||||||
|
|
||||||
template <> constexpr bool typeMatches<kernel32::FileObject>(const ObjectBase *o) noexcept {
|
template <> constexpr bool typeMatches<kernel32::FileObject>(const ObjectBase *o) noexcept {
|
||||||
return o && (o->flags & Of_File);
|
return o && (o->flags & Of_File);
|
||||||
}
|
}
|
||||||
|
|||||||
385
dll/ntdll.cpp
385
dll/ntdll.cpp
@@ -6,7 +6,9 @@
|
|||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "heap.h"
|
#include "heap.h"
|
||||||
|
#include "kernel32/fileapi.h"
|
||||||
#include "kernel32/internal.h"
|
#include "kernel32/internal.h"
|
||||||
|
#include "kernel32/minwinbase.h"
|
||||||
#include "kernel32/processthreadsapi.h"
|
#include "kernel32/processthreadsapi.h"
|
||||||
#include "modules.h"
|
#include "modules.h"
|
||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
@@ -14,7 +16,9 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -56,6 +60,93 @@ constexpr ULONG kOsBuildNumber = 0;
|
|||||||
constexpr ULONG kOsPlatformId = 2; // VER_PLATFORM_WIN32_NT
|
constexpr ULONG kOsPlatformId = 2; // VER_PLATFORM_WIN32_NT
|
||||||
constexpr BYTE kProductTypeWorkstation = 1; // VER_NT_WORKSTATION
|
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<LONGLONG>::max())) {
|
||||||
|
return std::numeric_limits<LONGLONG>::max();
|
||||||
|
}
|
||||||
|
return static_cast<LONGLONG>(ticks);
|
||||||
|
#else
|
||||||
|
long double ticks = static_cast<long double>(ts.tv_sec) * static_cast<long double>(kHundredNanosecondsPerSecond);
|
||||||
|
ticks += static_cast<long double>(ts.tv_nsec) / 100.0L;
|
||||||
|
ticks += static_cast<long double>(kUnixEpochAsFileTime);
|
||||||
|
if (ticks < 0.0L) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ticks > static_cast<long double>(std::numeric_limits<LONGLONG>::max())) {
|
||||||
|
return std::numeric_limits<LONGLONG>::max();
|
||||||
|
}
|
||||||
|
return static_cast<LONGLONG>(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) {
|
bool resolveProcessDetails(HANDLE processHandle, ProcessHandleDetails &details) {
|
||||||
if (kernel32::isPseudoCurrentProcessHandle(processHandle)) {
|
if (kernel32::isPseudoCurrentProcessHandle(processHandle)) {
|
||||||
details.pid = getpid();
|
details.pid = getpid();
|
||||||
@@ -262,7 +353,7 @@ NTSTATUS WINAPI NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Apc
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddress, ULONG_PTR ZeroBits,
|
NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, ULONG_PTR ZeroBits,
|
||||||
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) {
|
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("NtAllocateVirtualMemory(%p, %p, %lu, %p, %lu, %lu) ", ProcessHandle, BaseAddress, ZeroBits, RegionSize,
|
DEBUG_LOG("NtAllocateVirtualMemory(%p, %p, %lu, %p, %lu, %lu) ", ProcessHandle, BaseAddress, ZeroBits, RegionSize,
|
||||||
@@ -276,7 +367,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAdd
|
|||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *baseAddress = fromGuestPtr(*BaseAddress);
|
void *baseAddress = BaseAddress->get();
|
||||||
size_t regionSize = static_cast<size_t>(*RegionSize);
|
size_t regionSize = static_cast<size_t>(*RegionSize);
|
||||||
wibo::heap::VmStatus vmStatus = wibo::heap::virtualAlloc(
|
wibo::heap::VmStatus vmStatus = wibo::heap::virtualAlloc(
|
||||||
&baseAddress, ®ionSize, static_cast<DWORD>(AllocationType), static_cast<DWORD>(Protect));
|
&baseAddress, ®ionSize, static_cast<DWORD>(AllocationType), static_cast<DWORD>(Protect));
|
||||||
@@ -286,14 +377,14 @@ NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAdd
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
*BaseAddress = toGuestPtr(baseAddress);
|
*BaseAddress = baseAddress;
|
||||||
*RegionSize = static_cast<SIZE_T>(regionSize);
|
*RegionSize = static_cast<SIZE_T>(regionSize);
|
||||||
|
|
||||||
DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS);
|
DEBUG_LOG("-> 0x%x\n", STATUS_SUCCESS);
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddress, PSIZE_T NumberOfBytesToProtect,
|
NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, PSIZE_T NumberOfBytesToProtect,
|
||||||
ULONG NewAccessProtection, PULONG OldAccessProtection) {
|
ULONG NewAccessProtection, PULONG OldAccessProtection) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("NtProtectVirtualMemory(%p, %p, %p, %lu, %p) ", ProcessHandle, BaseAddress, NumberOfBytesToProtect,
|
DEBUG_LOG("NtProtectVirtualMemory(%p, %p, %p, %lu, %p) ", ProcessHandle, BaseAddress, NumberOfBytesToProtect,
|
||||||
@@ -307,7 +398,7 @@ NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddr
|
|||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *base = fromGuestPtr(*BaseAddress);
|
void *base = BaseAddress->get();
|
||||||
size_t length = static_cast<size_t>(*NumberOfBytesToProtect);
|
size_t length = static_cast<size_t>(*NumberOfBytesToProtect);
|
||||||
wibo::heap::VmStatus vmStatus =
|
wibo::heap::VmStatus vmStatus =
|
||||||
wibo::heap::virtualProtect(base, length, static_cast<DWORD>(NewAccessProtection), OldAccessProtection);
|
wibo::heap::virtualProtect(base, length, static_cast<DWORD>(NewAccessProtection), OldAccessProtection);
|
||||||
@@ -321,6 +412,290 @@ NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddr
|
|||||||
return 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<unsigned>(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<int32_t>(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<kernel32::FsObject>(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<PFILE_BASIC_INFORMATION>(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<PFILE_STANDARD_INFORMATION>(FileInformation);
|
||||||
|
unsigned long long allocation = static_cast<unsigned long long>(st.st_blocks) * 512ULL;
|
||||||
|
info->AllocationSize.QuadPart = static_cast<LONGLONG>(allocation);
|
||||||
|
info->EndOfFile.QuadPart = static_cast<LONGLONG>(st.st_size);
|
||||||
|
info->NumberOfLinks = static_cast<ULONG>(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<kernel32::FileObject>();
|
||||||
|
if (!file) {
|
||||||
|
status = STATUS_INVALID_PARAMETER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Length < sizeof(FILE_POSITION_INFORMATION)) {
|
||||||
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto info = reinterpret_cast<PFILE_POSITION_INFORMATION>(FileInformation);
|
||||||
|
info->CurrentByteOffset.QuadPart = static_cast<LONGLONG>(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<PFILE_NAME_INFORMATION>(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<ULONG>(bytesRequired);
|
||||||
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info->FileNameLength = static_cast<ULONG>(bytesRequired);
|
||||||
|
if (bytesRequired > 0) {
|
||||||
|
std::memcpy(info->FileName, wide.data(), bytesRequired);
|
||||||
|
}
|
||||||
|
IoStatusBlock->Information = static_cast<ULONG>(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<long long, std::ratio<1, 10000000>>;
|
||||||
|
auto now = std::chrono::system_clock::now().time_since_epoch();
|
||||||
|
auto sinceUnix = std::chrono::duration_cast<HundredNanoseconds>(now).count();
|
||||||
|
ULONGLONG fileTime = kUnixEpochAsFileTime + static_cast<ULONGLONG>(sinceUnix);
|
||||||
|
SystemTime->QuadPart = static_cast<LONGLONG>(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<ULONGLONG>(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<ULONG>(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) {
|
NTSTATUS WINAPI RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("RtlGetVersion(%p) ", lpVersionInformation);
|
DEBUG_LOG("RtlGetVersion(%p) ", lpVersionInformation);
|
||||||
|
|||||||
12
dll/ntdll.h
12
dll/ntdll.h
@@ -38,10 +38,18 @@ NTSTATUS WINAPI NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcR
|
|||||||
NTSTATUS WINAPI NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
|
NTSTATUS WINAPI NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
|
||||||
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset,
|
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset,
|
||||||
PULONG Key);
|
PULONG Key);
|
||||||
NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddress, ULONG_PTR ZeroBits,
|
NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, ULONG_PTR ZeroBits,
|
||||||
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
|
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
|
||||||
NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, GUEST_PTR *BaseAddress, PSIZE_T NumberOfBytesToProtect,
|
NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE ProcessHandle, guest_ptr<> *BaseAddress, PSIZE_T NumberOfBytesToProtect,
|
||||||
ULONG NewAccessProtection, PULONG OldAccessProtection);
|
ULONG NewAccessProtection, PULONG OldAccessProtection);
|
||||||
|
NTSTATUS WINAPI NtQueryInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
|
||||||
|
ULONG Length, FILE_INFORMATION_CLASS FileInformationClass);
|
||||||
|
NTSTATUS WINAPI NtQuerySystemTime(PLARGE_INTEGER SystemTime);
|
||||||
|
BOOLEAN WINAPI RtlTimeToSecondsSince1970(PLARGE_INTEGER Time, PULONG ElapsedSeconds);
|
||||||
|
VOID WINAPI RtlInitializeBitMap(PRTL_BITMAP BitMapHeader, PULONG BitMapBuffer, ULONG SizeOfBitMap);
|
||||||
|
VOID WINAPI RtlSetBits(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG NumberToSet);
|
||||||
|
BOOLEAN WINAPI RtlAreBitsSet(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length);
|
||||||
|
BOOLEAN WINAPI RtlAreBitsClear(PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length);
|
||||||
NTSTATUS WINAPI RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation);
|
NTSTATUS WINAPI RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation);
|
||||||
NTSTATUS WINAPI NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass,
|
NTSTATUS WINAPI NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass,
|
||||||
PVOID ProcessInformation, ULONG ProcessInformationLength,
|
PVOID ProcessInformation, ULONG ProcessInformationLength,
|
||||||
|
|||||||
@@ -55,7 +55,9 @@
|
|||||||
#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003)
|
#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003)
|
||||||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004)
|
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004)
|
||||||
#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002)
|
#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002)
|
||||||
|
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005)
|
||||||
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011)
|
#define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011)
|
||||||
|
#define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS)0xC0000024)
|
||||||
#define STATUS_PENDING ((NTSTATUS)0x00000103)
|
#define STATUS_PENDING ((NTSTATUS)0x00000103)
|
||||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
|
||||||
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
#define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9)
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ enum class ObjectType : uint16_t {
|
|||||||
enum ObjectFlags : uint16_t {
|
enum ObjectFlags : uint16_t {
|
||||||
Of_None = 0x0,
|
Of_None = 0x0,
|
||||||
Of_Waitable = 0x1,
|
Of_Waitable = 0x1,
|
||||||
Of_File = 0x2,
|
Of_FsObject = 0x2,
|
||||||
|
Of_File = 0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ObjectBase {
|
struct ObjectBase {
|
||||||
|
|||||||
@@ -29,9 +29,10 @@
|
|||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro LJMP64
|
.macro LJMP64
|
||||||
push CS_64 # 64-bit code segment (Linux)
|
// Annoyingly, we can't assemble this in Intel syntax
|
||||||
push offset 1f # 64-bit code offset
|
.att_syntax prefix
|
||||||
retf # far jump into 64-bit code
|
ljmp $CS_64, $1f
|
||||||
|
.intel_syntax noprefix
|
||||||
.code64
|
.code64
|
||||||
1:
|
1:
|
||||||
endbr64
|
endbr64
|
||||||
|
|||||||
103
src/types.h
103
src/types.h
@@ -50,11 +50,10 @@ inline GUEST_PTR toGuestPtr(const void *addr) {
|
|||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
return static_cast<GUEST_PTR>(addr64);
|
return static_cast<GUEST_PTR>(addr64);
|
||||||
}
|
}
|
||||||
inline void *fromGuestPtr(GUEST_PTR addr) { return reinterpret_cast<void *>(addr); }
|
|
||||||
#else
|
#else
|
||||||
inline GUEST_PTR toGuestPtr(const void *addr) { return static_cast<GUEST_PTR>(reinterpret_cast<unsigned long>(addr)); }
|
inline GUEST_PTR toGuestPtr(const void *addr) { return static_cast<GUEST_PTR>(reinterpret_cast<unsigned long>(addr)); }
|
||||||
inline void *fromGuestPtr(GUEST_PTR addr) { return reinterpret_cast<void *>(addr); }
|
|
||||||
#endif
|
#endif
|
||||||
|
template <typename T = void> inline T *fromGuestPtr(GUEST_PTR addr) { return reinterpret_cast<T *>(addr); }
|
||||||
|
|
||||||
using VOID = void;
|
using VOID = void;
|
||||||
using HANDLE = int;
|
using HANDLE = int;
|
||||||
@@ -81,8 +80,8 @@ using LONG = int;
|
|||||||
using PLONG = LONG *;
|
using PLONG = LONG *;
|
||||||
using ULONG = unsigned int;
|
using ULONG = unsigned int;
|
||||||
using PULONG = ULONG *;
|
using PULONG = ULONG *;
|
||||||
using LONGLONG = long long;
|
using LONGLONG __attribute__((aligned(8))) = long long;
|
||||||
using ULONGLONG = unsigned long long;
|
using ULONGLONG __attribute__((aligned(8))) = unsigned long long;
|
||||||
using LONG_PTR = int;
|
using LONG_PTR = int;
|
||||||
using ULONG_PTR = unsigned int;
|
using ULONG_PTR = unsigned int;
|
||||||
using UINT_PTR = unsigned int;
|
using UINT_PTR = unsigned int;
|
||||||
@@ -129,6 +128,48 @@ constexpr HANDLE NO_HANDLE = 0;
|
|||||||
using NTSTATUS = LONG;
|
using NTSTATUS = LONG;
|
||||||
using HRESULT = LONG;
|
using HRESULT = LONG;
|
||||||
|
|
||||||
|
template <typename T = void> struct guest_ptr {
|
||||||
|
GUEST_PTR ptr;
|
||||||
|
|
||||||
|
explicit guest_ptr(GUEST_PTR p) : ptr(p) {}
|
||||||
|
explicit guest_ptr(const T *p) : ptr(toGuestPtr(p)) {}
|
||||||
|
guest_ptr(const guest_ptr &p) = default;
|
||||||
|
guest_ptr(guest_ptr &&p) : ptr(p.ptr) {}
|
||||||
|
guest_ptr &operator=(T *p) {
|
||||||
|
ptr = toGuestPtr(p);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
guest_ptr &operator=(guest_ptr p) {
|
||||||
|
ptr = p.ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
[[nodiscard]] T *get() const { return reinterpret_cast<T *>(ptr); }
|
||||||
|
T &operator*() const { return *reinterpret_cast<T *>(ptr); }
|
||||||
|
T *operator->() const { return reinterpret_cast<T *>(ptr); }
|
||||||
|
operator T *() const { return reinterpret_cast<T *>(ptr); } // NOLINT(google-explicit-constructor)
|
||||||
|
operator bool() const { return ptr != GUEST_NULL; } // NOLINT(google-explicit-constructor)
|
||||||
|
T &operator[](SIZE_T index) const { return get()[index]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct guest_ptr<void> {
|
||||||
|
GUEST_PTR ptr;
|
||||||
|
|
||||||
|
explicit guest_ptr(GUEST_PTR p) : ptr(p) {}
|
||||||
|
explicit guest_ptr(void *p) : ptr(toGuestPtr(p)) {}
|
||||||
|
guest_ptr(const guest_ptr &p) = default;
|
||||||
|
guest_ptr(guest_ptr &&p) : ptr(p.ptr) {}
|
||||||
|
guest_ptr &operator=(void *p) {
|
||||||
|
ptr = toGuestPtr(p);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
guest_ptr &operator=(guest_ptr p) {
|
||||||
|
ptr = p.ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
[[nodiscard]] void *get() const { return reinterpret_cast<void *>(ptr); }
|
||||||
|
operator bool() const { return ptr != GUEST_NULL; } // NOLINT(google-explicit-constructor)
|
||||||
|
};
|
||||||
|
|
||||||
typedef union _LARGE_INTEGER {
|
typedef union _LARGE_INTEGER {
|
||||||
struct {
|
struct {
|
||||||
DWORD LowPart;
|
DWORD LowPart;
|
||||||
@@ -153,6 +194,60 @@ typedef union _ULARGE_INTEGER {
|
|||||||
ULONGLONG QuadPart;
|
ULONGLONG QuadPart;
|
||||||
} ULARGE_INTEGER, *PULARGE_INTEGER;
|
} ULARGE_INTEGER, *PULARGE_INTEGER;
|
||||||
|
|
||||||
|
typedef struct _RTL_BITMAP {
|
||||||
|
ULONG SizeOfBitMap;
|
||||||
|
guest_ptr<ULONG> Buffer;
|
||||||
|
} RTL_BITMAP, *PRTL_BITMAP;
|
||||||
|
|
||||||
|
enum FILE_INFORMATION_CLASS {
|
||||||
|
FileDirectoryInformation = 1,
|
||||||
|
FileFullDirectoryInformation = 2,
|
||||||
|
FileBothDirectoryInformation = 3,
|
||||||
|
FileBasicInformation = 4,
|
||||||
|
FileStandardInformation = 5,
|
||||||
|
FileInternalInformation = 6,
|
||||||
|
FileEaInformation = 7,
|
||||||
|
FileAccessInformation = 8,
|
||||||
|
FileNameInformation = 9,
|
||||||
|
FileRenameInformation = 10,
|
||||||
|
FileLinkInformation = 11,
|
||||||
|
FileNamesInformation = 12,
|
||||||
|
FileDispositionInformation = 13,
|
||||||
|
FilePositionInformation = 14,
|
||||||
|
FileFullEaInformation = 15,
|
||||||
|
FileModeInformation = 16,
|
||||||
|
FileAlignmentInformation = 17,
|
||||||
|
FileAllInformation = 18,
|
||||||
|
FileAllocationInformation = 19,
|
||||||
|
FileEndOfFileInformation = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _FILE_BASIC_INFORMATION {
|
||||||
|
LARGE_INTEGER CreationTime;
|
||||||
|
LARGE_INTEGER LastAccessTime;
|
||||||
|
LARGE_INTEGER LastWriteTime;
|
||||||
|
LARGE_INTEGER ChangeTime;
|
||||||
|
ULONG FileAttributes;
|
||||||
|
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
||||||
|
|
||||||
|
typedef struct _FILE_STANDARD_INFORMATION {
|
||||||
|
LARGE_INTEGER AllocationSize;
|
||||||
|
LARGE_INTEGER EndOfFile;
|
||||||
|
ULONG NumberOfLinks;
|
||||||
|
BOOLEAN DeletePending;
|
||||||
|
BOOLEAN Directory;
|
||||||
|
USHORT Reserved;
|
||||||
|
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
|
||||||
|
|
||||||
|
typedef struct _FILE_POSITION_INFORMATION {
|
||||||
|
LARGE_INTEGER CurrentByteOffset;
|
||||||
|
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
||||||
|
|
||||||
|
typedef struct _FILE_NAME_INFORMATION {
|
||||||
|
ULONG FileNameLength;
|
||||||
|
WCHAR FileName[1];
|
||||||
|
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
|
||||||
|
|
||||||
struct GUID {
|
struct GUID {
|
||||||
DWORD Data1;
|
DWORD Data1;
|
||||||
WORD Data2;
|
WORD Data2;
|
||||||
|
|||||||
106
test/test_ntdll_time.c
Normal file
106
test/test_ntdll_time.c
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#ifndef STATUS_SUCCESS
|
||||||
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const ULONGLONG kUnixEpochAsFileTime = 116444736000000000ULL;
|
||||||
|
static const ULONGLONG kHundredNsPerSecond = 10000000ULL;
|
||||||
|
|
||||||
|
typedef NTSTATUS(WINAPI *NtQuerySystemTimeFn)(PLARGE_INTEGER SystemTime);
|
||||||
|
typedef BOOLEAN(WINAPI *RtlTimeToSecondsSince1970Fn)(PLARGE_INTEGER Time, PULONG ElapsedSeconds);
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
NtQuerySystemTimeFn query_system_time;
|
||||||
|
RtlTimeToSecondsSince1970Fn time_to_seconds;
|
||||||
|
} gFns;
|
||||||
|
|
||||||
|
static FARPROC load_ntdll_proc(const char *name) {
|
||||||
|
static HMODULE ntdll;
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_CHECK(ntdll != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(ntdll, name);
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_functions_loaded(void) {
|
||||||
|
if (gFns.query_system_time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC proc = load_ntdll_proc("NtQuerySystemTime");
|
||||||
|
TEST_CHECK(sizeof(gFns.query_system_time) == sizeof(proc));
|
||||||
|
memcpy(&gFns.query_system_time, &proc, sizeof(gFns.query_system_time));
|
||||||
|
|
||||||
|
proc = load_ntdll_proc("RtlTimeToSecondsSince1970");
|
||||||
|
TEST_CHECK(sizeof(gFns.time_to_seconds) == sizeof(proc));
|
||||||
|
memcpy(&gFns.time_to_seconds, &proc, sizeof(gFns.time_to_seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONGLONG filetime_to_u64(const FILETIME *ft) {
|
||||||
|
ULARGE_INTEGER li;
|
||||||
|
li.LowPart = ft->dwLowDateTime;
|
||||||
|
li.HighPart = ft->dwHighDateTime;
|
||||||
|
return li.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_nt_query_system_time_matches_filetime(void) {
|
||||||
|
LARGE_INTEGER system_time = {.QuadPart = 0};
|
||||||
|
NTSTATUS status = gFns.query_system_time(&system_time);
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, status);
|
||||||
|
|
||||||
|
FILETIME ft = {0};
|
||||||
|
GetSystemTimeAsFileTime(&ft);
|
||||||
|
ULONGLONG api_time = filetime_to_u64(&ft);
|
||||||
|
ULONGLONG query_time = (ULONGLONG)system_time.QuadPart;
|
||||||
|
ULONGLONG delta = (api_time > query_time) ? (api_time - query_time) : (query_time - api_time);
|
||||||
|
TEST_CHECK_MSG(delta < 1000000ULL, "NtQuerySystemTime skew too large: %llu", (unsigned long long)delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_nt_query_system_time_null(void) {
|
||||||
|
NTSTATUS status = gFns.query_system_time(NULL);
|
||||||
|
TEST_CHECK_EQ((ULONG)STATUS_ACCESS_VIOLATION, (ULONG)status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rtl_time_to_seconds_success(void) {
|
||||||
|
LARGE_INTEGER system_time = {.QuadPart = 0};
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, gFns.query_system_time(&system_time));
|
||||||
|
|
||||||
|
ULONG seconds = 0;
|
||||||
|
TEST_CHECK(gFns.time_to_seconds(&system_time, &seconds));
|
||||||
|
ULONGLONG expected = ((ULONGLONG)system_time.QuadPart - kUnixEpochAsFileTime) / kHundredNsPerSecond;
|
||||||
|
TEST_CHECK_EQ(expected, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rtl_time_to_seconds_invalid_inputs(void) {
|
||||||
|
LARGE_INTEGER before_epoch = {.QuadPart = (LONGLONG)(kUnixEpochAsFileTime - kHundredNsPerSecond)};
|
||||||
|
ULONG seconds = 0;
|
||||||
|
TEST_CHECK_EQ(FALSE, gFns.time_to_seconds(&before_epoch, &seconds));
|
||||||
|
|
||||||
|
LARGE_INTEGER beyond_range = {
|
||||||
|
.QuadPart = (LONGLONG)(kUnixEpochAsFileTime + (0x1'00000000ULL * kHundredNsPerSecond))};
|
||||||
|
TEST_CHECK_EQ(FALSE, gFns.time_to_seconds(&beyond_range, &seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
ensure_functions_loaded();
|
||||||
|
TEST_CHECK(gFns.query_system_time);
|
||||||
|
TEST_CHECK(gFns.time_to_seconds);
|
||||||
|
|
||||||
|
test_nt_query_system_time_matches_filetime();
|
||||||
|
test_nt_query_system_time_null();
|
||||||
|
test_rtl_time_to_seconds_success();
|
||||||
|
test_rtl_time_to_seconds_invalid_inputs();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
166
test/test_ntqueryfile.c
Normal file
166
test/test_ntqueryfile.c
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
#ifndef STATUS_SUCCESS
|
||||||
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_INFO_LENGTH_MISMATCH
|
||||||
|
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_INVALID_HANDLE
|
||||||
|
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_INVALID_PARAMETER
|
||||||
|
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STATUS_OBJECT_TYPE_MISMATCH
|
||||||
|
#define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS)0xC0000024L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef NTSTATUS(WINAPI *NtQueryInformationFileFn)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||||
|
PVOID FileInformation, ULONG Length,
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass);
|
||||||
|
|
||||||
|
static NtQueryInformationFileFn gNtQueryInformationFile;
|
||||||
|
static char gTempPath[MAX_PATH];
|
||||||
|
static char gTempFile[MAX_PATH];
|
||||||
|
|
||||||
|
static FARPROC load_ntdll_proc(const char *name) {
|
||||||
|
static HMODULE ntdll;
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_CHECK(ntdll != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(ntdll, name);
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_loaded(void) {
|
||||||
|
if (gNtQueryInformationFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FARPROC proc = load_ntdll_proc("NtQueryInformationFile");
|
||||||
|
TEST_CHECK(sizeof(gNtQueryInformationFile) == sizeof(proc));
|
||||||
|
memcpy(&gNtQueryInformationFile, &proc, sizeof(gNtQueryInformationFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
static HANDLE create_temp_file(void) {
|
||||||
|
DWORD pathLen = GetTempPathA(sizeof(gTempPath), gTempPath);
|
||||||
|
TEST_CHECK(pathLen > 0 && pathLen < sizeof(gTempPath));
|
||||||
|
|
||||||
|
UINT unique = GetTempFileNameA(gTempPath, "NQI", 0, gTempFile);
|
||||||
|
TEST_CHECK(unique != 0);
|
||||||
|
|
||||||
|
HANDLE file = CreateFileA(gTempFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_basic_and_standard_information(HANDLE file, size_t bytesWritten) {
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
FILE_BASIC_INFORMATION basic;
|
||||||
|
NTSTATUS status =
|
||||||
|
gNtQueryInformationFile(file, &iosb, &basic, sizeof(basic), FileBasicInformation);
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(sizeof(basic), iosb.Information);
|
||||||
|
TEST_CHECK((basic.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) != 0);
|
||||||
|
TEST_CHECK(basic.CreationTime.QuadPart != 0);
|
||||||
|
TEST_CHECK(basic.LastWriteTime.QuadPart != 0);
|
||||||
|
|
||||||
|
FILE_STANDARD_INFORMATION standardInfo;
|
||||||
|
status = gNtQueryInformationFile(file, &iosb, &standardInfo, sizeof(standardInfo),
|
||||||
|
FileStandardInformation);
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(sizeof(standardInfo), iosb.Information);
|
||||||
|
TEST_CHECK_EQ((LONGLONG)bytesWritten, standardInfo.EndOfFile.QuadPart);
|
||||||
|
TEST_CHECK(standardInfo.AllocationSize.QuadPart >= (LONGLONG)bytesWritten);
|
||||||
|
TEST_CHECK_EQ(FALSE, standardInfo.DeletePending);
|
||||||
|
TEST_CHECK_EQ(FALSE, standardInfo.Directory);
|
||||||
|
TEST_CHECK(standardInfo.NumberOfLinks >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_position_information(HANDLE file, size_t expectedOffset) {
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
FILE_POSITION_INFORMATION positionInfo;
|
||||||
|
NTSTATUS status = gNtQueryInformationFile(file, &iosb, &positionInfo, sizeof(positionInfo),
|
||||||
|
FilePositionInformation);
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, status);
|
||||||
|
TEST_CHECK_EQ(sizeof(positionInfo), iosb.Information);
|
||||||
|
TEST_CHECK_EQ((LONGLONG)expectedOffset, positionInfo.CurrentByteOffset.QuadPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_file_name_information(HANDLE file) {
|
||||||
|
unsigned char buffer[sizeof(FILE_NAME_INFORMATION) + 512];
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
NTSTATUS status = gNtQueryInformationFile(file, &iosb, buffer, sizeof(buffer),
|
||||||
|
FileNameInformation);
|
||||||
|
TEST_CHECK_EQ(STATUS_SUCCESS, status);
|
||||||
|
PFILE_NAME_INFORMATION nameInfo = (PFILE_NAME_INFORMATION)buffer;
|
||||||
|
TEST_CHECK(nameInfo->FileNameLength > 0);
|
||||||
|
TEST_CHECK_EQ(sizeof(ULONG) + nameInfo->FileNameLength, iosb.Information);
|
||||||
|
|
||||||
|
size_t chars = nameInfo->FileNameLength / sizeof(WCHAR);
|
||||||
|
char narrow[512];
|
||||||
|
TEST_CHECK(chars < sizeof(narrow));
|
||||||
|
for (size_t i = 0; i < chars; ++i) {
|
||||||
|
WCHAR ch = nameInfo->FileName[i];
|
||||||
|
TEST_CHECK(ch < 0x80);
|
||||||
|
narrow[i] = (char)ch;
|
||||||
|
}
|
||||||
|
narrow[chars] = '\0';
|
||||||
|
|
||||||
|
const char *expected = strchr(gTempFile, ':');
|
||||||
|
if (expected) {
|
||||||
|
++expected;
|
||||||
|
} else {
|
||||||
|
expected = gTempFile;
|
||||||
|
}
|
||||||
|
TEST_CHECK_STR_EQ(expected, narrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_invalid_cases(HANDLE file) {
|
||||||
|
IO_STATUS_BLOCK iosb;
|
||||||
|
FILE_BASIC_INFORMATION basic;
|
||||||
|
NTSTATUS status = gNtQueryInformationFile(file, &iosb, &basic, sizeof(basic) - 1,
|
||||||
|
FileBasicInformation);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_INFO_LENGTH_MISMATCH, status);
|
||||||
|
|
||||||
|
status = gNtQueryInformationFile(INVALID_HANDLE_VALUE, &iosb, &basic, sizeof(basic),
|
||||||
|
FileBasicInformation);
|
||||||
|
TEST_CHECK_EQ((NTSTATUS)STATUS_OBJECT_TYPE_MISMATCH, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
ensure_loaded();
|
||||||
|
TEST_CHECK(gNtQueryInformationFile != NULL);
|
||||||
|
|
||||||
|
HANDLE file = create_temp_file();
|
||||||
|
|
||||||
|
const char *data = "ntqueryfile";
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(file, data, (DWORD)strlen(data), &written, NULL));
|
||||||
|
|
||||||
|
test_basic_and_standard_information(file, written);
|
||||||
|
test_position_information(file, written);
|
||||||
|
|
||||||
|
TEST_CHECK(SetFilePointer(file, 3, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||||
|
test_position_information(file, 3);
|
||||||
|
test_file_name_information(file);
|
||||||
|
test_invalid_cases(file);
|
||||||
|
|
||||||
|
CloseHandle(file);
|
||||||
|
DeleteFileA(gTempFile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
117
test/test_rtl_bitmap.c
Normal file
117
test/test_rtl_bitmap.c
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test_assert.h"
|
||||||
|
|
||||||
|
typedef struct _TEST_RTL_BITMAP {
|
||||||
|
ULONG SizeOfBitMap;
|
||||||
|
PULONG Buffer;
|
||||||
|
} TEST_RTL_BITMAP;
|
||||||
|
|
||||||
|
typedef VOID(WINAPI *RtlInitializeBitMapFn)(TEST_RTL_BITMAP *, PULONG, ULONG);
|
||||||
|
typedef VOID(WINAPI *RtlSetBitsFn)(TEST_RTL_BITMAP *, ULONG, ULONG);
|
||||||
|
typedef BOOLEAN(WINAPI *RtlAreBitsSetFn)(TEST_RTL_BITMAP *, ULONG, ULONG);
|
||||||
|
typedef BOOLEAN(WINAPI *RtlAreBitsClearFn)(TEST_RTL_BITMAP *, ULONG, ULONG);
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
RtlInitializeBitMapFn initialize;
|
||||||
|
RtlSetBitsFn set_bits;
|
||||||
|
RtlAreBitsSetFn are_bits_set;
|
||||||
|
RtlAreBitsClearFn are_bits_clear;
|
||||||
|
} gRtlBitmapFns;
|
||||||
|
|
||||||
|
static FARPROC load_ntdll_proc(const char *name) {
|
||||||
|
static HMODULE ntdll;
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (!ntdll) {
|
||||||
|
ntdll = LoadLibraryW(L"ntdll.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_CHECK(ntdll != NULL);
|
||||||
|
FARPROC proc = GetProcAddress(ntdll, name);
|
||||||
|
TEST_CHECK(proc != NULL);
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_rtl_bitmap_functions(void) {
|
||||||
|
if (gRtlBitmapFns.initialize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC proc = load_ntdll_proc("RtlInitializeBitMap");
|
||||||
|
TEST_CHECK(sizeof(gRtlBitmapFns.initialize) == sizeof(proc));
|
||||||
|
memcpy(&gRtlBitmapFns.initialize, &proc, sizeof(gRtlBitmapFns.initialize));
|
||||||
|
|
||||||
|
proc = load_ntdll_proc("RtlSetBits");
|
||||||
|
TEST_CHECK(sizeof(gRtlBitmapFns.set_bits) == sizeof(proc));
|
||||||
|
memcpy(&gRtlBitmapFns.set_bits, &proc, sizeof(gRtlBitmapFns.set_bits));
|
||||||
|
|
||||||
|
proc = load_ntdll_proc("RtlAreBitsSet");
|
||||||
|
TEST_CHECK(sizeof(gRtlBitmapFns.are_bits_set) == sizeof(proc));
|
||||||
|
memcpy(&gRtlBitmapFns.are_bits_set, &proc, sizeof(gRtlBitmapFns.are_bits_set));
|
||||||
|
|
||||||
|
proc = load_ntdll_proc("RtlAreBitsClear");
|
||||||
|
TEST_CHECK(sizeof(gRtlBitmapFns.are_bits_clear) == sizeof(proc));
|
||||||
|
memcpy(&gRtlBitmapFns.are_bits_clear, &proc, sizeof(gRtlBitmapFns.are_bits_clear));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_initialize_sets_header(void) {
|
||||||
|
ULONG buffer[2] = {0};
|
||||||
|
TEST_RTL_BITMAP bitmap;
|
||||||
|
memset(&bitmap, 0xCC, sizeof(bitmap));
|
||||||
|
|
||||||
|
gRtlBitmapFns.initialize(&bitmap, buffer, 64);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(64u, bitmap.SizeOfBitMap);
|
||||||
|
TEST_CHECK(bitmap.Buffer == buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_set_bits_and_queries(void) {
|
||||||
|
ULONG buffer[2] = {0};
|
||||||
|
TEST_RTL_BITMAP bitmap;
|
||||||
|
gRtlBitmapFns.initialize(&bitmap, buffer, 64);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_set(&bitmap, 1, 3));
|
||||||
|
gRtlBitmapFns.set_bits(&bitmap, 1, 3);
|
||||||
|
TEST_CHECK_EQ(TRUE, gRtlBitmapFns.are_bits_set(&bitmap, 1, 3));
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_set(&bitmap, 0, 4));
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_set(&bitmap, 0, 0));
|
||||||
|
TEST_CHECK_EQ(TRUE, gRtlBitmapFns.are_bits_clear(&bitmap, 4, 4));
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_clear(&bitmap, 4, 0));
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_clear(&bitmap, 1, 1));
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(0x0000000Eu, buffer[0]);
|
||||||
|
TEST_CHECK_EQ(0x00000000u, buffer[1]);
|
||||||
|
|
||||||
|
ULONG snapshot = buffer[0];
|
||||||
|
gRtlBitmapFns.set_bits(&bitmap, 0, 0);
|
||||||
|
TEST_CHECK_EQ(snapshot, buffer[0]);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_set(&bitmap, 60, 8));
|
||||||
|
TEST_CHECK_EQ(TRUE, gRtlBitmapFns.are_bits_clear(&bitmap, 8, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_are_bits_clear_after_setting(void) {
|
||||||
|
ULONG buffer[2] = {0};
|
||||||
|
TEST_RTL_BITMAP bitmap;
|
||||||
|
gRtlBitmapFns.initialize(&bitmap, buffer, 64);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(TRUE, gRtlBitmapFns.are_bits_clear(&bitmap, 16, 8));
|
||||||
|
gRtlBitmapFns.set_bits(&bitmap, 16, 8);
|
||||||
|
TEST_CHECK_EQ(FALSE, gRtlBitmapFns.are_bits_clear(&bitmap, 16, 8));
|
||||||
|
TEST_CHECK_EQ(TRUE, gRtlBitmapFns.are_bits_set(&bitmap, 16, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
load_rtl_bitmap_functions();
|
||||||
|
TEST_CHECK(gRtlBitmapFns.initialize);
|
||||||
|
TEST_CHECK(gRtlBitmapFns.set_bits);
|
||||||
|
TEST_CHECK(gRtlBitmapFns.are_bits_set);
|
||||||
|
TEST_CHECK(gRtlBitmapFns.are_bits_clear);
|
||||||
|
|
||||||
|
test_initialize_sets_header();
|
||||||
|
test_set_bits_and_queries();
|
||||||
|
test_are_bits_clear_after_setting();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user