[Kernel] - Change OddObj to X_DISPATCH_HEADER

- Change OddObj to X_DISPATCH_HEADER
- Add unknown X_KWAIT_REASON flag
- Fixed InitializeGuestObject mistake
- Replaces unknowns in kthread using nukernel as reference
This commit is contained in:
The-Little-Wolf
2026-04-11 12:47:06 -07:00
committed by Radosław Gliński
parent c2674b19d2
commit dc4db67f9c
9 changed files with 74 additions and 61 deletions

View File

@@ -1527,15 +1527,17 @@ void KernelState::InitializeKernelGuestGlobals() {
SetProcessTLSVars(system_process, 32, 0, 0); SetProcessTLSVars(system_process, 32, 0, 0);
uint32_t oddobject_offset = uint32_t oddobject_offset =
kernel_guest_globals_ + offsetof(KernelGuestGlobals, OddObj); kernel_guest_globals_ +
offsetof(KernelGuestGlobals, XboxKernelDefaultObject);
// init unknown object // init unknown object
block->OddObj.field0 = 0x1000000; block->XboxKernelDefaultObject.type = DISPATCHER_AUTO_RESET_EVENT;
block->OddObj.field4 = 1; block->XboxKernelDefaultObject.signal_state = 1;
block->OddObj.points_to_self = block->XboxKernelDefaultObject.wait_list.flink_ptr =
oddobject_offset + offsetof(X_UNKNOWN_TYPE_REFED, points_to_self); oddobject_offset + offsetof(X_DISPATCH_HEADER, wait_list.flink_ptr);
block->OddObj.points_to_prior = block->OddObj.points_to_self; block->XboxKernelDefaultObject.wait_list.blink_ptr =
block->XboxKernelDefaultObject.wait_list.flink_ptr;
// init thread object // init thread object
block->ExThreadObjectType.pool_tag = 0x65726854; block->ExThreadObjectType.pool_tag = 0x65726854;

View File

@@ -96,21 +96,13 @@ struct TerminateNotification {
// a bit like the timers on KUSER_SHARED on normal win32 // a bit like the timers on KUSER_SHARED on normal win32
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
struct X_TIME_STAMP_BUNDLE { struct X_TIME_STAMP_BUNDLE {
// according to Nukernl these are BE
uint64_t interrupt_time; uint64_t interrupt_time;
// i assume system_time is in 100 ns intervals like on win32 // i assume system_time is in 100 ns intervals like on win32
uint64_t system_time; uint64_t system_time;
uint32_t tick_count; uint32_t tick_count;
uint32_t padding; uint32_t padding;
}; };
struct X_UNKNOWN_TYPE_REFED {
xe::be<uint32_t> field0;
xe::be<uint32_t> field4;
// this is definitely a LIST_ENTRY?
xe::be<uint32_t> points_to_self; // this field points to itself
xe::be<uint32_t>
points_to_prior; // points to the previous field, which points to itself
};
static_assert_size(X_UNKNOWN_TYPE_REFED, 16);
struct KernelGuestGlobals { struct KernelGuestGlobals {
X_OBJECT_TYPE ExThreadObjectType; X_OBJECT_TYPE ExThreadObjectType;
@@ -125,7 +117,7 @@ struct KernelGuestGlobals {
X_OBJECT_TYPE ObSymbolicLinkObjectType; X_OBJECT_TYPE ObSymbolicLinkObjectType;
// a constant buffer that some object types' "unknown_size_or_object" field // a constant buffer that some object types' "unknown_size_or_object" field
// points to // points to
X_UNKNOWN_TYPE_REFED OddObj; X_DISPATCH_HEADER XboxKernelDefaultObject;
X_KPROCESS idle_process; // X_PROCTYPE_IDLE. runs in interrupt contexts. is X_KPROCESS idle_process; // X_PROCTYPE_IDLE. runs in interrupt contexts. is
// also the context the kernel starts in? // also the context the kernel starts in?
X_KPROCESS title_process; // X_PROCTYPE_TITLE X_KPROCESS title_process; // X_PROCTYPE_TITLE

View File

@@ -676,7 +676,7 @@ struct X_POOL_ALLOC_HEADER {
}; };
uint32_t xeAllocatePoolTypeWithTag(PPCContext* context, uint32_t size, uint32_t xeAllocatePoolTypeWithTag(PPCContext* context, uint32_t size,
uint32_t tag, uint32_t zero) { uint32_t tag, uint32_t pool_selector) {
if (size <= 0xFD8) { if (size <= 0xFD8) {
uint32_t adjusted_size = size + sizeof(X_POOL_ALLOC_HEADER); uint32_t adjusted_size = size + sizeof(X_POOL_ALLOC_HEADER);
@@ -694,9 +694,9 @@ uint32_t xeAllocatePoolTypeWithTag(PPCContext* context, uint32_t size,
} }
dword_result_t ExAllocatePoolTypeWithTag_entry(dword_t size, dword_t tag, dword_result_t ExAllocatePoolTypeWithTag_entry(dword_t size, dword_t tag,
dword_t zero, dword_t pool_selector,
const ppc_context_t& context) { const ppc_context_t& context) {
return xeAllocatePoolTypeWithTag(context, size, tag, zero); return xeAllocatePoolTypeWithTag(context, size, tag, pool_selector);
} }
DECLARE_XBOXKRNL_EXPORT1(ExAllocatePoolTypeWithTag, kMemory, kImplemented); DECLARE_XBOXKRNL_EXPORT1(ExAllocatePoolTypeWithTag, kMemory, kImplemented);

View File

@@ -50,7 +50,7 @@ uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size,
dword_result_t xeMmQueryStatistics( dword_result_t xeMmQueryStatistics(
pointer_t<X_MM_QUERY_STATISTICS_RESULT> stats_ptr); pointer_t<X_MM_QUERY_STATISTICS_RESULT> stats_ptr);
uint32_t xeAllocatePoolTypeWithTag(PPCContext* context, uint32_t size, uint32_t xeAllocatePoolTypeWithTag(PPCContext* context, uint32_t size,
uint32_t tag, uint32_t zero); uint32_t tag, uint32_t pool_selector);
void xeFreePool(PPCContext* context, uint32_t base_address); void xeFreePool(PPCContext* context, uint32_t base_address);

View File

@@ -544,7 +544,7 @@ static_assert_size(X_RTL_CRITICAL_SECTION, 28);
void xeRtlInitializeCriticalSection(X_RTL_CRITICAL_SECTION* cs, void xeRtlInitializeCriticalSection(X_RTL_CRITICAL_SECTION* cs,
uint32_t cs_ptr) { uint32_t cs_ptr) {
cs->header.type = 1; // EventSynchronizationObject (auto reset) cs->header.type = X_DISPATCHER_FLAGS::DISPATCHER_AUTO_RESET_EVENT;
cs->header.absolute = 0; // spin count div 256 cs->header.absolute = 0; // spin count div 256
cs->header.signal_state = 0; cs->header.signal_state = 0;
cs->lock_count = -1; cs->lock_count = -1;
@@ -567,7 +567,7 @@ X_STATUS xeRtlInitializeCriticalSectionAndSpinCount(X_RTL_CRITICAL_SECTION* cs,
spin_count_div_256 = 255; spin_count_div_256 = 255;
} }
cs->header.type = 1; // EventSynchronizationObject (auto reset) cs->header.type = X_DISPATCHER_FLAGS::DISPATCHER_AUTO_RESET_EVENT;
cs->header.absolute = spin_count_div_256; cs->header.absolute = spin_count_div_256;
cs->header.signal_state = 0; cs->header.signal_state = 0;
cs->lock_count = -1; cs->lock_count = -1;

View File

@@ -692,12 +692,12 @@ DECLARE_XBOXKRNL_EXPORT2(NtClearEvent, kThreading, kImplemented,
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff552150(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552150(v=vs.85).aspx
void KeInitializeSemaphore_entry(pointer_t<X_KSEMAPHORE> semaphore_ptr, void KeInitializeSemaphore_entry(pointer_t<X_KSEMAPHORE> semaphore_ptr,
dword_t count, dword_t limit) { dword_t count, dword_t limit) {
semaphore_ptr->header.type = 5; // SemaphoreObject semaphore_ptr->header.type = X_DISPATCHER_FLAGS::DISPATCHER_SEMAPHORE;
semaphore_ptr->header.signal_state = (uint32_t)count; semaphore_ptr->header.signal_state = (uint32_t)count;
semaphore_ptr->limit = (uint32_t)limit; semaphore_ptr->limit = (uint32_t)limit;
auto sem = XObject::GetNativeObject<XSemaphore>(kernel_state(), semaphore_ptr, auto sem = XObject::GetNativeObject<XSemaphore>(
5 /* SemaphoreObject */); kernel_state(), semaphore_ptr, X_DISPATCHER_FLAGS::DISPATCHER_SEMAPHORE);
if (!sem) { if (!sem) {
assert_always(); assert_always();
return; return;
@@ -1014,7 +1014,7 @@ dword_result_t KeWaitForMultipleObjects_entry(
dword_t count, lpdword_t objects_ptr, dword_t wait_type, dword_t count, lpdword_t objects_ptr, dword_t wait_type,
dword_t wait_reason, dword_t processor_mode, dword_t alertable, dword_t wait_reason, dword_t processor_mode, dword_t alertable,
lpqword_t timeout_ptr, lpvoid_t wait_block_array_ptr) { lpqword_t timeout_ptr, lpvoid_t wait_block_array_ptr) {
assert_true(wait_type <= 1); assert_true(wait_type <= X_KWAIT_REASON::WaitAny);
assert_true(count <= 64); assert_true(count <= 64);
object_ref<XObject> objects[64]; object_ref<XObject> objects[64];
@@ -1049,7 +1049,7 @@ uint32_t xeNtWaitForMultipleObjectsEx(uint32_t count, xe::be<uint32_t>* handles,
uint32_t wait_type, uint32_t wait_mode, uint32_t wait_type, uint32_t wait_mode,
uint32_t alertable, uint32_t alertable,
uint64_t* timeout_ptr) { uint64_t* timeout_ptr) {
assert_true(wait_type <= 1); assert_true(wait_type <= X_KWAIT_REASON::WaitAny);
assert_true(count <= 64); assert_true(count <= 64);
object_ref<XObject> objects[64]; object_ref<XObject> objects[64];
@@ -1091,7 +1091,8 @@ dword_result_t NtWaitForMultipleObjectsEx_entry(
dword_t count, lpdword_t handles, dword_t wait_type, dword_t wait_mode, dword_t count, lpdword_t handles, dword_t wait_type, dword_t wait_mode,
dword_t alertable, lpqword_t timeout_ptr) { dword_t alertable, lpqword_t timeout_ptr) {
uint64_t timeout = timeout_ptr ? static_cast<uint64_t>(*timeout_ptr) : 0u; uint64_t timeout = timeout_ptr ? static_cast<uint64_t>(*timeout_ptr) : 0u;
if (!count || count > 64 || (wait_type != 1 && wait_type)) { if (!count || count > 64 ||
(wait_type != X_KWAIT_REASON::WaitAny && wait_type)) {
return X_STATUS_INVALID_PARAMETER; return X_STATUS_INVALID_PARAMETER;
} }
return xeNtWaitForMultipleObjectsEx(count, handles, wait_type, wait_mode, return xeNtWaitForMultipleObjectsEx(count, handles, wait_type, wait_mode,
@@ -1871,7 +1872,7 @@ dword_result_t KeSetPriorityThread_entry(pointer_t<X_KTHREAD> thread_ptr,
return 0; return 0;
} }
if (thread_ptr->header.type != 6) { if (thread_ptr->header.type != X_DISPATCHER_FLAGS::DISPATCHER_THREAD) {
XELOGW("{}: Invalid object type: {}", __func__, thread_ptr->header.type); XELOGW("{}: Invalid object type: {}", __func__, thread_ptr->header.type);
} }

View File

@@ -34,10 +34,21 @@ class KernelState;
template <typename T> template <typename T>
class object_ref; class object_ref;
enum X_DISPATCHER_FLAGS : uint8_t {
DISPATCHER_MANUAL_RESET_EVENT = 0,
DISPATCHER_AUTO_RESET_EVENT = 1, // EventSynchronizationObject
DISPATCHER_MUTANT = 2,
DISPATCHER_QUEUE = 4,
DISPATCHER_SEMAPHORE = 5, // SemaphoreObject
DISPATCHER_THREAD = 6,
DISPATCHER_MANUAL_RESET_TIMER = 8,
DISPATCHER_AUTO_RESET_TIMER = 9,
};
// https://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html // https://www.nirsoft.net/kernel_struct/vista/DISPATCHER_HEADER.html
typedef struct { typedef struct {
struct { struct {
uint8_t type; uint8_t type; // X_DISPATCHER_FLAGS
union { union {
uint8_t abandoned; uint8_t abandoned;

View File

@@ -180,21 +180,25 @@ static uint8_t GetFakeCpuNumber(uint8_t proc_mask) {
void XThread::InitializeGuestObject() { void XThread::InitializeGuestObject() {
auto guest_thread = guest_object<X_KTHREAD>(); auto guest_thread = guest_object<X_KTHREAD>();
auto thread_guest_ptr = guest_object(); auto thread_guest_ptr = guest_object();
guest_thread->header.type = 6; guest_thread->header.type = X_DISPATCHER_FLAGS::DISPATCHER_THREAD;
guest_thread->suspend_count = guest_thread->suspend_count =
(creation_params_.creation_flags & X_CREATE_SUSPENDED) ? 1 : 0; (creation_params_.creation_flags & X_CREATE_SUSPENDED) ? 1 : 0;
guest_thread->unk_10 = (thread_guest_ptr + 0x10); guest_thread->mutants_list.flink_ptr = (thread_guest_ptr + 0x10);
guest_thread->unk_14 = (thread_guest_ptr + 0x10); guest_thread->mutants_list.blink_ptr = (thread_guest_ptr + 0x10);
auto timer_wait_header_list_entry = memory()->HostToGuestVirtual(
&guest_thread->wait_timeout_timer.header.wait_list);
guest_thread->wait_timeout_block.wait_list_entry.flink_ptr = guest_thread->wait_timeout_block.wait_list_entry.flink_ptr =
thread_guest_ptr + 0x20; timer_wait_header_list_entry;
guest_thread->wait_timeout_block.wait_list_entry.blink_ptr = guest_thread->wait_timeout_block.wait_list_entry.blink_ptr =
thread_guest_ptr + 0x20; timer_wait_header_list_entry;
guest_thread->wait_timeout_block.thread = thread_guest_ptr; guest_thread->wait_timeout_block.thread = thread_guest_ptr;
uint32_t v6 = thread_guest_ptr + 0x18; guest_thread->wait_timeout_block.object =
guest_thread->wait_timeout_block.wait_result_xstatus = 0x0100; memory()->HostToGuestVirtual(&guest_thread->wait_timeout_timer);
guest_thread->wait_timeout_block.wait_type = 0x0201; guest_thread->wait_timeout_block.wait_result_xstatus = X_STATUS_TIMEOUT;
guest_thread->wait_timeout_block.object = v6; guest_thread->wait_timeout_block.wait_type = X_KWAIT_REASON::WaitAny;
guest_thread->stack_base = (this->stack_base_); guest_thread->stack_base = (this->stack_base_);
guest_thread->stack_limit = (this->stack_limit_); guest_thread->stack_limit = (this->stack_limit_);
guest_thread->stack_kernel = (this->stack_base_ - 240); guest_thread->stack_kernel = (this->stack_base_ - 240);

View File

@@ -48,17 +48,6 @@ enum IRQL_FLAGS : uint8_t {
IRQL_HIGHEST = 124 IRQL_HIGHEST = 124
}; };
enum X_DISPATCHER_FLAGS {
DISPATCHER_MANUAL_RESET_EVENT = 0,
DISPATCHER_AUTO_RESET_EVENT = 1,
DISPATCHER_MUTANT = 2,
DISPATCHER_QUEUE = 4,
DISPATCHER_SEMAPHORE = 5,
DISPATCHER_THREAD = 6,
DISPATCHER_MANUAL_RESET_TIMER = 8,
DISPATCHER_AUTO_RESET_TIMER = 9,
};
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke/kthread_state.htm // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke/kthread_state.htm
enum X_KTHREAD_STATE_FLAGS : uint8_t { enum X_KTHREAD_STATE_FLAGS : uint8_t {
KTHREAD_STATE_INITIALIZED = 0, KTHREAD_STATE_INITIALIZED = 0,
@@ -222,12 +211,22 @@ struct X_KPCR {
uint8_t unk_2AC[0x2C]; // 0x2AC uint8_t unk_2AC[0x2C]; // 0x2AC
}; };
#ifdef WAIT_ANY struct X_KMUTANT {
#undef WAIT_ANY X_DISPATCH_HEADER header; // 0x0
#endif X_LIST_ENTRY unk_list; // 0x10
enum : uint16_t { TypedGuestPointer<X_KTHREAD> owner; // 0x18
WAIT_ALL = 0, bool abandoned; // 0x1C
WAIT_ANY = 1, // these might just be padding
uint8_t unk_1D; // 0x1D
uint8_t unk_1E; // 0x1E
uint8_t unk_1F; // 0x1F
};
static_assert_size(X_KMUTANT, 0x20);
enum X_KWAIT_REASON : uint16_t {
WaitAll = 0,
WaitAny = 1,
WaitUnk3 = 3,
}; };
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke_x/kwait_block.htm // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke_x/kwait_block.htm
@@ -243,7 +242,7 @@ struct X_KWAIT_BLOCK {
// is satisfied // is satisfied
xe::be<uint16_t> wait_result_xstatus; xe::be<uint16_t> wait_result_xstatus;
// WAIT_ALL or WAIT_ANY // WAIT_ALL or WAIT_ANY
xe::be<uint16_t> wait_type; xe::be<X_KWAIT_REASON> wait_type;
}; };
static_assert_size(X_KWAIT_BLOCK, 0x18); static_assert_size(X_KWAIT_BLOCK, 0x18);
@@ -258,9 +257,9 @@ struct X_KTIMER {
static_assert_size(X_KTIMER, 0x28); static_assert_size(X_KTIMER, 0x28);
struct X_KTHREAD { struct X_KTHREAD {
X_DISPATCH_HEADER header; // 0x0 X_DISPATCH_HEADER header; // 0x0
xe::be<uint32_t> unk_10; // 0x10 util::X_TYPED_LIST<X_KMUTANT, offsetof(X_KMUTANT, unk_list)>
xe::be<uint32_t> unk_14; // 0x14 mutants_list; // 0x10
X_KTIMER wait_timeout_timer; // 0x18 X_KTIMER wait_timeout_timer; // 0x18
X_KWAIT_BLOCK wait_timeout_block; // 0x40 X_KWAIT_BLOCK wait_timeout_block; // 0x40
uint8_t unk_58[0x4]; // 0x58 uint8_t unk_58[0x4]; // 0x58
@@ -295,7 +294,11 @@ struct X_KTHREAD {
xe::be<uint32_t> msr_mask; // 0x9C xe::be<uint32_t> msr_mask; // 0x9C
xe::be<X_STATUS> wait_result; // 0xA0 xe::be<X_STATUS> wait_result; // 0xA0
uint8_t wait_irql; // 0xA4 uint8_t wait_irql; // 0xA4
uint8_t unk_A5[0xB]; // 0xA5 uint8_t processor_mode; // 0xA5
uint8_t wait_next; // 0xA6
uint8_t wait_reason; // 0xA7
TypedGuestPointer<X_KWAIT_BLOCK> wait_blocks; // 0xA8
uint8_t unk_AC[4]; // 0xAC
int32_t apc_disable_count; // 0xB0 int32_t apc_disable_count; // 0xB0
xe::be<int32_t> quantum; // 0xB4 xe::be<int32_t> quantum; // 0xB4
uint8_t saturation_increment; // 0xB8 uint8_t saturation_increment; // 0xB8