Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions include/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,14 @@ inline void DebugPrintInternal(DEBUG_LEVEL level, DEBUG_COMPONENT component,
pos += _vsnprintf_s(buffer + pos, bufferSize - pos, _TRUNCATE, format, args);
va_end(args);

/* Ensure newline at end */
if (pos > 0 && buffer[pos - 1] != '\n') {
if (pos < bufferSize - 2) {
buffer[pos++] = '\n';
buffer[pos] = '\0';
}
/* Ensure null termination and newline at end */
if (pos < 0) pos = 0;
if (pos >= bufferSize) pos = bufferSize - 1;
buffer[pos] = '\0';

if (pos > 0 && buffer[pos - 1] != '\n' && pos < bufferSize - 2) {
buffer[pos++] = '\n';
buffer[pos] = '\0';
}

/* Lock only for output - formatting was done lock-free above */
Expand Down
8 changes: 8 additions & 0 deletions include/NtStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@
#define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS)0xC0000039L)
#endif

#ifndef STATUS_OBJECT_NAME_COLLISION
#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
#endif

#ifndef STATUS_SHARING_VIOLATION
#define STATUS_SHARING_VIOLATION ((NTSTATUS)0xC0000043L)
#endif

// Helper macros
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
Expand Down
59 changes: 51 additions & 8 deletions src/File.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,61 @@ NTSTATUS ZwCreateFile(

if (*FileHandle == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
IoStatusBlock->Status = STATUS_UNSUCCESSFUL;
IoStatusBlock->Information = 0;

if (error == ERROR_FILE_NOT_FOUND) {
return STATUS_OBJECT_NAME_NOT_FOUND;
} else if (error == ERROR_ACCESS_DENIED) {
return STATUS_ACCESS_DENIED;
} else {
return STATUS_UNSUCCESSFUL;
switch (error) {
case ERROR_FILE_NOT_FOUND:
IoStatusBlock->Status = STATUS_OBJECT_NAME_NOT_FOUND;
return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_PATH_NOT_FOUND:
IoStatusBlock->Status = STATUS_OBJECT_PATH_NOT_FOUND;
return STATUS_OBJECT_PATH_NOT_FOUND;
case ERROR_ACCESS_DENIED:
IoStatusBlock->Status = STATUS_ACCESS_DENIED;
return STATUS_ACCESS_DENIED;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
IoStatusBlock->Status = STATUS_OBJECT_NAME_COLLISION;
return STATUS_OBJECT_NAME_COLLISION;
case ERROR_SHARING_VIOLATION:
IoStatusBlock->Status = STATUS_SHARING_VIOLATION;
return STATUS_SHARING_VIOLATION;
default:
IoStatusBlock->Status = STATUS_UNSUCCESSFUL;
return STATUS_UNSUCCESSFUL;
}
}

IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = FILE_OPENED; /* Simplified */

/* Determine Information based on disposition and whether file existed */
{
DWORD lastError = GetLastError();
switch (CreateDisposition) {
case FILE_SUPERSEDE:
IoStatusBlock->Information = FILE_SUPERSEDED;
break;
case FILE_OPEN:
IoStatusBlock->Information = FILE_OPENED;
break;
case FILE_CREATE:
IoStatusBlock->Information = FILE_CREATED;
break;
case FILE_OPEN_IF:
/* CREATE_ALWAYS/OPEN_ALWAYS set ERROR_ALREADY_EXISTS when file existed */
IoStatusBlock->Information = (lastError == ERROR_ALREADY_EXISTS) ? FILE_OPENED : FILE_CREATED;
break;
case FILE_OVERWRITE:
IoStatusBlock->Information = FILE_OVERWRITTEN;
break;
case FILE_OVERWRITE_IF:
IoStatusBlock->Information = (lastError == ERROR_ALREADY_EXISTS) ? FILE_OVERWRITTEN : FILE_CREATED;
break;
default:
IoStatusBlock->Information = FILE_OPENED;
break;
}
}

return STATUS_SUCCESS;
}
27 changes: 15 additions & 12 deletions src/Registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,25 +755,28 @@ NTSTATUS ZwEnumerateValueKey(
return STATUS_SUCCESS;
}

/* Helper: convert common Win32 registry errors to NTSTATUS */
static NTSTATUS RegistryErrorToNtstatus(LSTATUS status) {
switch (status) {
case ERROR_SUCCESS: return STATUS_SUCCESS;
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
case ERROR_NO_MORE_ITEMS: return STATUS_NO_MORE_ENTRIES;
default: return STATUS_UNSUCCESSFUL;
}
}

// ZwDeleteKey: Deletes a registry key using RegDeleteKeyW. Only works for empty keys (not recursive).
NTSTATUS ZwDeleteKey(HANDLE KeyHandle) {
// This deletes the key that KeyHandle refers to
// We need to close the handle after the key is deleted
LSTATUS status = RegDeleteKeyW((HKEY)KeyHandle, L"");
NTSTATUS ntStatus = RegistryErrorToNtstatus(status);

if (status != ERROR_SUCCESS) {
// Try to close the handle to avoid leaks even if delete failed
RegCloseKey((HKEY)KeyHandle);
return status;
}

// Key was deleted, now close the handle (safe because RegDeleteKeyW doesn't invalidate it)
RegCloseKey((HKEY)KeyHandle);
return STATUS_SUCCESS;
return ntStatus;
}

NTSTATUS ZwDeleteKeyEx(HKEY KeyHandle, LPCWSTR SubKey) {
// RegDeleteKeyW returns ERROR_SUCCESS (0) on success, map to STATUS_SUCCESS (0)
LSTATUS status = RegDeleteKeyW(KeyHandle, SubKey);
return (status == ERROR_SUCCESS) ? STATUS_SUCCESS : status;
return RegistryErrorToNtstatus(status);
}
132 changes: 56 additions & 76 deletions src/Resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,57 +121,48 @@ ExAcquireResourceExclusiveLite(
)
{
ERESOURCE_THREAD CurrentThread;
BOOLEAN Result = FALSE;

if (!Resource)
return FALSE;

// Get the current thread ID as the resource thread
CurrentThread = (ERESOURCE_THREAD)GetCurrentThreadId();

// Enter the critical section if Wait is TRUE, otherwise try to enter
if (Wait) {
EnterCriticalSection(&Resource->CriticalSection);
}
else if (!TryEnterCriticalSection(&Resource->CriticalSection)) {
return FALSE;
}

// Check if the resource is already owned
if (Resource->ActiveCount != 0) {
// If owned exclusively by the current thread, allow recursive exclusive acquisition
if (IsOwnedExclusive(Resource) &&
(Resource->OwnerThreads[0].OwnerThread == CurrentThread)) {
Resource->OwnerThreads[0].OwnerCount += 1;
Result = TRUE;
for (;;) {
if (Wait) {
EnterCriticalSection(&Resource->CriticalSection);
}
else {
// Resource is owned by another thread or shared - cannot acquire exclusive
if (Wait == FALSE) {
Result = FALSE;
else if (!TryEnterCriticalSection(&Resource->CriticalSection)) {
return FALSE;
}

if (Resource->ActiveCount != 0) {
if (IsOwnedExclusive(Resource) &&
(Resource->OwnerThreads[0].OwnerThread == CurrentThread)) {
Resource->OwnerThreads[0].OwnerCount += 1;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
else if (Wait == FALSE) {
LeaveCriticalSection(&Resource->CriticalSection);
return FALSE;
}
else {
// For simplicity in this user-mode implementation:
// If we need to wait and we're here, we know we're holding the critical section,
// but the resource is owned by someone else. We'll release and retry.
/* Owned by another thread - release CS and retry */
LeaveCriticalSection(&Resource->CriticalSection);
Sleep(1); // Yield to other threads
return ExAcquireResourceExclusiveLite(Resource, Wait);
Sleep(1);
continue;
}
}
else {
/* Resource is free - take it exclusively */
Resource->Flag |= ResourceOwnedExclusive;
Resource->OwnerThreads[0].OwnerThread = CurrentThread;
Resource->OwnerThreads[0].OwnerCount = 1;
Resource->ActiveCount = 1;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
}
else {
// Resource is not owned, so we can take it
Resource->Flag |= ResourceOwnedExclusive;
Resource->OwnerThreads[0].OwnerThread = CurrentThread;
Resource->OwnerThreads[0].OwnerCount = 1;
Resource->ActiveCount = 1;
Result = TRUE;
}

// Always release the critical section
LeaveCriticalSection(&Resource->CriticalSection);
return Result;
}

BOOLEAN
Expand All @@ -180,53 +171,42 @@ ExAcquireResourceSharedLite(
IN BOOLEAN Wait
)
{
ERESOURCE_THREAD CurrentThread;
BOOLEAN Result = FALSE;

if (!Resource)
return FALSE;

// Get the current thread ID as the resource thread
CurrentThread = (ERESOURCE_THREAD)GetCurrentThreadId();

// Enter the critical section if Wait is TRUE, otherwise try to enter
if (Wait) {
EnterCriticalSection(&Resource->CriticalSection);
}
else if (!TryEnterCriticalSection(&Resource->CriticalSection)) {
return FALSE;
}
for (;;) {
if (Wait) {
EnterCriticalSection(&Resource->CriticalSection);
}
else if (!TryEnterCriticalSection(&Resource->CriticalSection)) {
return FALSE;
}

// Check if the resource is already owned
if (Resource->ActiveCount != 0) {
if (IsOwnedExclusive(Resource)) {
if (Wait == FALSE) {
Result = FALSE;
}
else {
// For simplicity in this user-mode implementation:
// If we need to wait and we're here, we release and retry
LeaveCriticalSection(&Resource->CriticalSection);
Sleep(1); // Yield to other threads
return ExAcquireResourceSharedLite(Resource, Wait);
if (Resource->ActiveCount != 0) {
if (IsOwnedExclusive(Resource)) {
if (Wait == FALSE) {
LeaveCriticalSection(&Resource->CriticalSection);
return FALSE;
}
else {
/* Owned exclusively by another thread - release CS and retry */
LeaveCriticalSection(&Resource->CriticalSection);
Sleep(1);
continue;
}
}
/* Owned shared - add ourselves */
Resource->ActiveCount++;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
// It's owned shared, so we can add ourselves as another shared owner
else {
Resource->ActiveCount++;
Result = TRUE;
/* Resource is free - take it shared */
Resource->ActiveCount = 1;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
}
else {
// Resource is not owned, so we can take it shared
Resource->ActiveCount = 1;
Result = TRUE;
}

// Always leave the critical section when acquiring shared
LeaveCriticalSection(&Resource->CriticalSection);

return Result;
}

VOID
Expand Down
4 changes: 2 additions & 2 deletions tests/test_file_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ TEST_F(FileIOTest, ZwCreateFile_ValidParameters) {
EXPECT_NE(fileHandle, nullptr);
EXPECT_NE(fileHandle, INVALID_HANDLE_VALUE);
EXPECT_EQ(ioStatus.Status, STATUS_SUCCESS);
EXPECT_EQ(ioStatus.Information, FILE_OPENED);
EXPECT_EQ(ioStatus.Information, FILE_CREATED);

if (fileHandle && fileHandle != INVALID_HANDLE_VALUE) {
CloseHandle(fileHandle);
Expand Down Expand Up @@ -143,7 +143,7 @@ TEST_F(FileIOTest, ZwCreateFile_FileNotFound) {
);

EXPECT_EQ(status, STATUS_OBJECT_NAME_NOT_FOUND);
EXPECT_EQ(ioStatus.Status, STATUS_UNSUCCESSFUL);
EXPECT_EQ(ioStatus.Status, STATUS_OBJECT_NAME_NOT_FOUND);
}

TEST_F(FileIOTest, ZwCreateFile_InvalidParameters) {
Expand Down
7 changes: 2 additions & 5 deletions tests/test_zwdeletekey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,8 @@ TEST_F(ZwDeleteKeyTest, FailsToDeleteNonEmptyKey) {

DEBUG_INFO(DEBUG_COMPONENT_REGISTRY, "ZwDeleteKey on non-empty key returned status: 0x%08X", status);

// The status 0x5 is ERROR_ACCESS_DENIED (Windows error code)
// We need to check for both NTSTATUS and Windows error codes
bool deletionFailed = (
status == ERROR_ACCESS_DENIED
); // Any failure status
// ZwDeleteKey now returns proper NTSTATUS codes
bool deletionFailed = !NT_SUCCESS(status);

if (status == STATUS_SUCCESS) {
// If it succeeded, this indicates a limitation of the user-mode simulation
Expand Down
Loading