Skip to content

Commit

Permalink
Merge pull request #104 from RadWolfie/impl-object-types-test
Browse files Browse the repository at this point in the history
Implement object type tests
  • Loading branch information
ergo720 authored Mar 18, 2024
2 parents b4f297c + 6edbb98 commit 91f23de
Show file tree
Hide file tree
Showing 16 changed files with 356 additions and 73 deletions.
73 changes: 73 additions & 0 deletions src/assertions/object_type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <xboxkrnl/xboxkrnl.h>

#include "util/output.h"
#include "defines.h"

static BOOL assert_object_type_ex(POBJECT_TYPE object_type,
ULONG pool_tag,
BOOL has_close,
BOOL has_delete,
BOOL has_parse,
int func_line)
{
ASSERT_HEADER;

BOOLEAN is_address_valid;
GEN_CHECK_EX(object_type->AllocateProcedure, ExAllocatePoolWithTag, ".AllocateProcedure", func_line);
GEN_CHECK_EX(object_type->FreeProcedure, ExFreePool, ".FreeProcedure", func_line);
// NOTE: Some object type has Close/Delete/Parse functions assigned.
GEN_CHECK_EX(object_type->CloseProcedure != NULL, has_close, ".CloseProcedure", func_line);
if (has_close) {
is_address_valid = MmIsAddressValid(object_type->CloseProcedure);
GEN_CHECK_EX(is_address_valid, TRUE, ".CloseProcedure valid", func_line);
}
GEN_CHECK_EX(object_type->DeleteProcedure != NULL, has_delete, ".DeleteProcedure", func_line);
if (has_delete) {
is_address_valid = MmIsAddressValid(object_type->DeleteProcedure);
GEN_CHECK_EX(is_address_valid, TRUE, ".DeleteProcedure valid", func_line);
}
GEN_CHECK_EX(object_type->ParseProcedure != NULL, has_parse, ".ParseProcedure", func_line);
if (has_parse) {
is_address_valid = MmIsAddressValid(object_type->ParseProcedure);
GEN_CHECK_EX(is_address_valid, TRUE, ".ParseProcedure valid", func_line);
}
// NOTE: DefaultObject check has been moved to assert_object_header_type_ex function. Because it needs the object's address.
GEN_CHECK_EX(object_type->PoolTag, pool_tag, ".PoolTag", func_line);

return test_passed;
}
#define assert_object_type(object_type, pool_tag, has_close, has_delete, has_parse) \
assert_object_type_ex(object_type, pool_tag, has_close, has_delete, has_parse, __LINE__)

static BOOL assert_object_header_type_ex(POBJECT_TYPE object_type,
HANDLE object_handle,
int func_line)
{
ASSERT_HEADER;

PVOID type_object;
NTSTATUS status = ObReferenceObjectByHandle(object_handle, object_type, &type_object);
GEN_CHECK_EX(type_object != NULL, TRUE, "type_object", func_line);
if (NT_SUCCESS(status)) {
POBJECT_HEADER event_object_header = OBJECT_TO_OBJECT_HEADER(type_object);
// NOTE: DefaultObject's relative offset or address (either within kernel executable
// or allocated by ObCreateObject) is not exposed through kernel APIs.
// We can only verify if the absolute address is valid with a call to the
// MmIsAddressValid function.
BOOLEAN is_address_valid;
if ((LONG_PTR)object_type->DefaultObject >= 0) {
is_address_valid = MmIsAddressValid((PUCHAR)type_object + (LONG_PTR)object_type->DefaultObject);
}
else {
is_address_valid = MmIsAddressValid(type_object);
}
GEN_CHECK_EX(is_address_valid, TRUE, ".DefaultObject valid", func_line);

GEN_CHECK_EX(event_object_header->Type, object_type, "object_header->Type", func_line);
ObfDereferenceObject(type_object);
}

return test_passed;
}
#define assert_object_header_type(object_type, object_handle) \
assert_object_header_type_ex(object_type, object_handle, __LINE__)
8 changes: 0 additions & 8 deletions src/tests/ex/ExEventObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/ex/ExMutantObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/ex/ExSemaphoreObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/ex/ExTimerObjectType.c

This file was deleted.

77 changes: 77 additions & 0 deletions src/tests/ex/suite/object_type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <xboxkrnl/xboxkrnl.h>
#include <synchapi.h>
#include <handleapi.h>

#include "util/output.h"
#include "assertions/object_type.h"

TEST_FUNC(ExEventObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ExEventObjectType, 'vevE', FALSE, FALSE, FALSE);
HANDLE event_handle = CreateEventA(NULL, TRUE, FALSE, "ExEventObjectType");
GEN_CHECK(event_handle != INVALID_HANDLE_VALUE, TRUE, "event_handle");
if (event_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&ExEventObjectType, event_handle);
BOOL close = CloseHandle(event_handle);
GEN_CHECK(close, TRUE, "close handle");
}

TEST_END();
}

TEST_FUNC(ExMutantObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ExMutantObjectType, 'atuM', FALSE, TRUE, FALSE);
HANDLE mutant_handle = CreateMutexA(NULL, TRUE, "ExMutantObjectType");
GEN_CHECK(mutant_handle != INVALID_HANDLE_VALUE, TRUE, "mutant_handle");
if (mutant_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&ExMutantObjectType, mutant_handle);
BOOL close = CloseHandle(mutant_handle);
GEN_CHECK(close, TRUE, "close handle");
}

TEST_END();
}

TEST_FUNC(ExSemaphoreObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ExSemaphoreObjectType, 'ameS', FALSE, FALSE, FALSE);
HANDLE semaphore_handle = CreateSemaphore(NULL, 0, 1, "ExSemaphoreObjectType");
GEN_CHECK(semaphore_handle != INVALID_HANDLE_VALUE, TRUE, "semaphore_handle");
if (semaphore_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&ExSemaphoreObjectType, semaphore_handle);
BOOL close = CloseHandle(semaphore_handle);
GEN_CHECK(close, TRUE, "close handle");
}

TEST_END();
}

TEST_FUNC(ExTimerObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ExTimerObjectType, 'emiT', FALSE, TRUE, FALSE);
// NOTE: There's no such thing as CreateTimer for Windows, it is done through SetTimer.
ANSI_STRING obj_name;
RtlInitAnsiString(&obj_name, "ExTimerObjectType");
OBJECT_ATTRIBUTES obj_attr;
InitializeObjectAttributes(&obj_attr, &obj_name, OBJ_OPENIF, ObWin32NamedObjectsDirectory(), NULL);
// --------
HANDLE timer_handle;
NTSTATUS status = NtCreateTimer(&timer_handle, &obj_attr, NotificationTimer);
GEN_CHECK(timer_handle != INVALID_HANDLE_VALUE, TRUE, "timer_handle");
if (timer_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&ExTimerObjectType, timer_handle);
BOOL close = CloseHandle(timer_handle);
GEN_CHECK(close, TRUE, "close handle");
}

TEST_END();
}
8 changes: 0 additions & 8 deletions src/tests/io/IoCompletionObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/io/IoDeviceObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/io/IoFileObjectType.c

This file was deleted.

82 changes: 82 additions & 0 deletions src/tests/io/suite/object_type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <xboxkrnl/xboxkrnl.h>
#include <synchapi.h>
#include <fileapi.h>
#include <handleapi.h>

#include "util/output.h"
#include "assertions/object_type.h"
#include "util/device_dummy.h"

TEST_FUNC(IoCompletionObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&IoCompletionObjectType, 'pmoC', FALSE, TRUE, FALSE);
HANDLE completion_handle;
NTSTATUS status = NtCreateIoCompletion(&completion_handle, STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3, NULL, 0);
GEN_CHECK(completion_handle != INVALID_HANDLE_VALUE, TRUE, "completion_handle");
if (completion_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&IoCompletionObjectType, completion_handle);
PVOID key_context = NULL, apc_context = NULL;
IO_STATUS_BLOCK status_block = { 0 };
status = NtSetIoCompletion(completion_handle, &key_context, &apc_context, STATUS_SUCCESS, status_block.Information);
GEN_CHECK(status, STATUS_SUCCESS, "status return");
LARGE_INTEGER timeout = { .QuadPart = 0 };
status = NtRemoveIoCompletion(completion_handle, &key_context, &apc_context, &status_block, &timeout);
GEN_CHECK(status, STATUS_SUCCESS, "status return");
GEN_CHECK(status_block.Status, STATUS_SUCCESS, "status block");
}

TEST_END();
}

TEST_FUNC(IoDeviceObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&IoDeviceObjectType, 'iveD', FALSE, FALSE, TRUE);
ANSI_STRING obj_name;
RtlInitAnsiString(&obj_name, "\\Device\\IoDeviceObjectType");
NTSTATUS status = IoCreateDevice(&dummy_driver_object, 0, &obj_name, FILE_DEVICE_CD_ROM, FALSE, &dummy_device_object);
GEN_CHECK(status, STATUS_SUCCESS, "status return");
if (NT_SUCCESS(status)) {
OBJECT_ATTRIBUTES obj_attr;
InitializeObjectAttributes(&obj_attr, &obj_name, OBJ_OPENIF, NULL, NULL);
HANDLE device_handle;
status = ObOpenObjectByName(&obj_attr, NULL, NULL, &device_handle);
GEN_CHECK(status, STATUS_SUCCESS, "status return");
GEN_CHECK(device_handle != INVALID_HANDLE_VALUE, TRUE, "device_handle");
if (device_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&IoDeviceObjectType, device_handle);
BOOL close = CloseHandle(device_handle);
GEN_CHECK(close, TRUE, "close handle");
}
IoDeleteDevice(dummy_device_object);
dummy_device_object = NULL;
}

TEST_END();
}

TEST_FUNC(IoFileObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&IoFileObjectType, 'eliF', TRUE, TRUE, TRUE);
HANDLE file_handle = CreateFileA("D:\\IoFileObjectType",
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
GEN_CHECK(file_handle != INVALID_HANDLE_VALUE, TRUE, "file_handle");
if (file_handle != INVALID_HANDLE_VALUE) {
test_passed &= assert_object_header_type(&IoFileObjectType, file_handle);
BOOL close = CloseHandle(file_handle);
GEN_CHECK(close, TRUE, "close handle");
BOOL delete = DeleteFileA("D:\\IoFileObjectType");
GEN_CHECK(delete, TRUE, "delete file");
}

TEST_END();
}
8 changes: 0 additions & 8 deletions src/tests/ob/ObDirectoryObjectType.c

This file was deleted.

8 changes: 0 additions & 8 deletions src/tests/ob/ObSymbolicLinkObjectType.c

This file was deleted.

49 changes: 49 additions & 0 deletions src/tests/ob/suite/object_type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <xboxkrnl/xboxkrnl.h>
#include <nxdk/mount.h>
#include <handleapi.h>

#include "util/output.h"
#include "assertions/object_type.h"

TEST_FUNC(ObDirectoryObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ObDirectoryObjectType, 'eriD', FALSE, FALSE, FALSE);
OBJECT_ATTRIBUTES obj_attr;
InitializeObjectAttributes(&obj_attr, NULL, OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
HANDLE directory_handle;
NTSTATUS status = NtOpenDirectoryObject(&directory_handle, &obj_attr);
if (NT_SUCCESS(status)) {
test_passed &= assert_object_header_type(&ObDirectoryObjectType, directory_handle);
BOOL close = CloseHandle(directory_handle);
GEN_CHECK(close, TRUE, "close handle");
}

TEST_END();
}

TEST_FUNC(ObSymbolicLinkObjectType)
{
TEST_BEGIN();

test_passed &= assert_object_type(&ObSymbolicLinkObjectType, 'bmyS', FALSE, TRUE, FALSE);
ANSI_STRING obj_name;
RtlInitAnsiString(&obj_name, "\\??\\A:");
bool is_mount = nxMountDrive(obj_name.Buffer[4], "\\Device\\CdRom0");
GEN_CHECK(is_mount, TRUE, "is_mount");
OBJECT_ATTRIBUTES obj_attr;
InitializeObjectAttributes(&obj_attr, &obj_name, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE symlink_handle;
NTSTATUS status = NtOpenSymbolicLinkObject(&symlink_handle, &obj_attr);
if (NT_SUCCESS(status)) {
test_passed &= assert_object_header_type(&ObSymbolicLinkObjectType, symlink_handle);
BOOL close = CloseHandle(symlink_handle);
GEN_CHECK(close, TRUE, "close handle");
}
if (is_mount) {
nxUnmountDrive(obj_name.Buffer[4]);
}

TEST_END();
}
Loading

0 comments on commit 91f23de

Please sign in to comment.