[KERNEL] - Replacing Unk

- Changes here are from Chrispy's Nukernel
This commit is contained in:
The-Little-Wolf 2025-07-16 08:01:54 -07:00
parent fd82727be6
commit 20ab2a7af3
8 changed files with 174 additions and 99 deletions

View File

@ -1274,7 +1274,7 @@ void KernelState::InitializeProcess(X_KPROCESS* process, uint32_t type,
process->unk_19 = unk_19;
process->unk_1A = unk_1A;
util::XeInitializeListHead(&process->thread_list, thread_list_guest_ptr);
process->unk_0C = 60;
process->quantum = 60;
// doubt any guest code uses this ptr, which i think probably has something to
// do with the page table
process->clrdataa_masked_ptr = 0;
@ -1374,7 +1374,7 @@ void KernelState::InitializeKernelGuestGlobals() {
auto idle_process = memory()->TranslateVirtual<X_KPROCESS*>(GetIdleProcess());
InitializeProcess(idle_process, X_PROCTYPE_IDLE, 0, 0, 0);
idle_process->unk_0C = 0x7F;
idle_process->quantum = 0x7F;
auto system_process =
memory()->TranslateVirtual<X_KPROCESS*>(GetSystemProcess());
InitializeProcess(system_process, X_PROCTYPE_SYSTEM, 2, 5, 9);

View File

@ -58,7 +58,8 @@ struct X_KPROCESS {
// list of threads in this process, guarded by the spinlock above
X_LIST_ENTRY thread_list;
xe::be<uint32_t> unk_0C;
// quantum value assigned to each thread of the process
xe::be<int32_t> quantum;
// kernel sets this to point to a section of size 0x2F700 called CLRDATAA,
// except it clears bit 31 of the pointer. in 17559 the address is 0x801C0000,
// so it sets this ptr to 0x1C0000

View File

@ -32,7 +32,7 @@ struct XTASK_MESSAGE {
};
struct XAM_TASK_ARGS {
be<uint32_t> value1;
be<uint32_t> flags;
be<uint32_t> value2;
// i think there might be another value here, it might be padding
};
@ -48,7 +48,7 @@ dword_result_t XamTaskSchedule_entry(lpvoid_t callback,
if (optional_ptr) {
auto option = ctx->TranslateVirtual<XAM_TASK_ARGS*>(optional_ptr);
auto v1 = option->value1;
auto v1 = option->flags;
auto v2 = option->value2; // typically 0?
XELOGI("Got xam task args: v1 = {:08X}, v2 = {:08X}", v1.get(), v2.get());

View File

@ -797,14 +797,14 @@ dword_result_t NtCreateMutant_entry(
}
DECLARE_XBOXKRNL_EXPORT1(NtCreateMutant, kThreading, kImplemented);
dword_result_t NtReleaseMutant_entry(dword_t mutant_handle, dword_t unknown) {
dword_result_t NtReleaseMutant_entry(dword_t mutant_handle,
lpdword_t previous_count) {
// This doesn't seem to be supported.
// int32_t previous_count_ptr = SHIM_GET_ARG_32(2);
// Whatever arg 1 is all games seem to set it to 0, so whether it's
// abandon or wait we just say false. Which is good, cause they are
// both ignored.
assert_zero(unknown);
uint32_t priority_increment = 0;
bool abandon = false;
bool wait = false;
@ -861,11 +861,11 @@ DECLARE_XBOXKRNL_EXPORT1(NtCreateTimer, kThreading, kImplemented);
dword_result_t NtSetTimerEx_entry(dword_t timer_handle, lpqword_t due_time_ptr,
lpvoid_t routine_ptr /*PTIMERAPCROUTINE*/,
dword_t unk_one, lpvoid_t routine_arg,
dword_t mode, lpvoid_t routine_arg,
dword_t resume, dword_t period_ms,
dword_t unk_zero) {
assert_true(unk_one == 1);
assert_true(unk_zero == 0);
lpdword_t unk_zero) {
assert_true(mode == 1);
assert_true(!unk_zero);
uint64_t due_time = *due_time_ptr;

View File

@ -349,7 +349,7 @@ void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) {
// Memory uninitialized, so don't bother with the check.
if (!uninitialized) {
assert_true(!(header->wait_list_blink & 0x1));
assert_true(!(header->wait_list.blink_ptr & 0x1));
}
// Stash pointer in struct.
@ -370,8 +370,8 @@ object_ref<XObject> XObject::GetNativeObject(KernelState* kernel_state,
// we don't have to worry about PPC code poking the struct. Because of that,
// we init on first use, store our handle in the struct, and dereference it
// each time.
// We identify this by setting wait_list_flink to a magic value. When set,
// wait_list_blink will hold a handle to our object.
// We identify this by setting wait_list.flink_ptr to a magic value. When set,
// wait_list.blink_ptr will hold a handle to our object.
if (!already_locked) {
global_critical_region::mutex().lock();
}
@ -383,10 +383,10 @@ object_ref<XObject> XObject::GetNativeObject(KernelState* kernel_state,
as_type = header->type;
}
if (header->wait_list_flink == kXObjSignature) {
if (header->wait_list.flink_ptr == kXObjSignature) {
// Already initialized.
// TODO: assert if the type of the object != as_type
uint32_t handle = header->wait_list_blink;
uint32_t handle = header->wait_list.blink_ptr;
result = kernel_state->object_table()
->LookupObject<XObject>(handle, true)
.release();

View File

@ -57,8 +57,7 @@ typedef struct {
};
xe::be<uint32_t> signal_state;
xe::be<uint32_t> wait_list_flink;
xe::be<uint32_t> wait_list_blink;
X_LIST_ENTRY wait_list;
} X_DISPATCH_HEADER;
static_assert_size(X_DISPATCH_HEADER, 0x10);
@ -245,8 +244,8 @@ class XObject {
// Stash native pointer into X_DISPATCH_HEADER
static void StashHandle(X_DISPATCH_HEADER* header, uint32_t handle) {
header->wait_list_flink = kXObjSignature;
header->wait_list_blink = handle;
header->wait_list.flink_ptr = kXObjSignature;
header->wait_list.blink_ptr = handle;
}
static uint32_t TimeoutTicksToMs(int64_t timeout_ticks);

View File

@ -174,12 +174,15 @@ void XThread::InitializeGuestObject() {
guest_thread->unk_10 = (thread_guest_ptr + 0x10);
guest_thread->unk_14 = (thread_guest_ptr + 0x10);
guest_thread->unk_40 = (thread_guest_ptr + 0x20);
guest_thread->unk_44 = (thread_guest_ptr + 0x20);
guest_thread->unk_48 = (thread_guest_ptr);
guest_thread->wait_timeout_block.wait_list_entry.flink_ptr =
thread_guest_ptr + 0x20;
guest_thread->wait_timeout_block.wait_list_entry.blink_ptr =
thread_guest_ptr + 0x20;
guest_thread->wait_timeout_block.thread = thread_guest_ptr;
uint32_t v6 = thread_guest_ptr + 0x18;
*(uint32_t*)&guest_thread->unk_54 = 16777729;
guest_thread->unk_4C = (v6);
guest_thread->wait_timeout_block.wait_result_xstatus = 0x0100;
guest_thread->wait_timeout_block.wait_type = 0x0201;
guest_thread->wait_timeout_block.object = v6;
guest_thread->stack_base = (this->stack_base_);
guest_thread->stack_limit = (this->stack_limit_);
guest_thread->stack_kernel = (this->stack_base_ - 240);
@ -207,14 +210,14 @@ void XThread::InitializeGuestObject() {
guest_thread->process = process_info_block_address;
guest_thread->stack_alloc_base = this->stack_base_;
guest_thread->create_time = Clock::QueryGuestSystemTime();
guest_thread->unk_144 = thread_guest_ptr + 324;
guest_thread->unk_148 = thread_guest_ptr + 324;
guest_thread->timer_list.flink_ptr = thread_guest_ptr + 324;
guest_thread->timer_list.blink_ptr = thread_guest_ptr + 324;
guest_thread->thread_id = this->thread_id_;
guest_thread->start_address = this->creation_params_.start_address;
guest_thread->unk_154 = thread_guest_ptr + 340;
guest_thread->unk_154.flink_ptr = thread_guest_ptr + 340;
uint32_t v9 = thread_guest_ptr;
guest_thread->last_error = 0;
guest_thread->unk_158 = v9 + 340;
guest_thread->unk_154.blink_ptr = v9 + 340;
guest_thread->creation_flags = this->creation_params_.creation_flags;
guest_thread->unk_17C = 1;

View File

@ -29,6 +29,39 @@ constexpr fourcc_t kThreadSaveSignature = make_fourcc("THRD");
class XEvent;
enum IRQL_FLAGS : uint8_t {
IRQL_PASSIVE = 0,
IRQL_APC = 1,
IRQL_DISPATCH = 2,
IRQL_DPC = 3,
IRQL_AUDIO = 68, // used a few times in the audio driver
IRQL_CLOCK = 116, // irql used by the clock interrupt
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
enum X_KTHREAD_STATE_FLAGS : uint8_t {
KTHREAD_STATE_INITIALIZED = 0,
KTHREAD_STATE_READY = 1,
KTHREAD_STATE_RUNNING = 2,
KTHREAD_STATE_STANDBY = 3,
KTHREAD_STATE_TERMINATED = 4,
KTHREAD_STATE_WAITING = 5,
KTHREAD_STATE_UNKNOWN = 6, //"Transition" except that makes no sense here, so
// 6 likely has a different meaning on xboxkrnl
};
constexpr uint32_t X_CREATE_SUSPENDED = 0x00000001;
constexpr uint32_t X_TLS_OUT_OF_INDEXES = UINT32_MAX;
@ -82,7 +115,7 @@ struct X_KTHREAD;
struct X_KPROCESS;
struct X_KPRCB {
TypedGuestPointer<X_KTHREAD> current_thread; // 0x0
TypedGuestPointer<X_KTHREAD> unk_4; // 0x4
TypedGuestPointer<X_KTHREAD> next_thread; // 0x4
TypedGuestPointer<X_KTHREAD> idle_thread; // 0x8
uint8_t current_cpu; // 0xC
uint8_t unk_D[3]; // 0xD
@ -104,14 +137,14 @@ struct X_KPRCB {
xe::be<uint32_t> unk_3C; // 0x3C
xe::be<uint32_t> dpc_related_40; // 0x40
// must be held to modify any dpc-related fields in the kprcb
xe::be<uint32_t> dpc_lock; // 0x44
X_LIST_ENTRY queued_dpcs_list_head; // 0x48
xe::be<uint32_t> dpc_active; // 0x50
X_KSPINLOCK spin_lock; // 0x54
xe::be<uint32_t> unk_58; // 0x58
xe::be<uint32_t> dpc_lock; // 0x44
X_LIST_ENTRY queued_dpcs_list_head; // 0x48
xe::be<uint32_t> dpc_active; // 0x50
X_KSPINLOCK spin_lock; // 0x54
TypedGuestPointer<X_KTHREAD> running_idle_thread; // 0x58
// definitely scheduler related
X_SINGLE_LIST_ENTRY unk_5C; // 0x5C
xe::be<uint32_t> unk_60; // 0x60
X_SINGLE_LIST_ENTRY enqueued_threads_list; // 0x5C
xe::be<uint32_t> has_ready_thread_by_priority; // 0x60
// i think the following mask has something to do with the array that comes
// after
xe::be<uint32_t> unk_mask_64; // 0x64
@ -123,7 +156,7 @@ struct X_KPRCB {
// thread_exit_dpc's routine drains this list and frees each threads threadid,
// kernel stack and dereferences the thread
X_LIST_ENTRY terminating_threads_list; // 0x184
XDPC unk_18C; // 0x18C
XDPC switch_thread_processor_dpc; // 0x18C
};
// Processor Control Region
struct X_KPCR {
@ -132,19 +165,25 @@ struct X_KPCR {
union {
xe::be<uint16_t> software_interrupt_state; // 0x8
struct {
uint8_t unknown_8; // 0x8
uint8_t generic_software_interrupt; // 0x8
uint8_t apc_software_interrupt_state; // 0x9
};
};
uint8_t unk_0A[2]; // 0xA
xe::be<uint16_t> unk_0A; // 0xA
uint8_t processtype_value_in_dpc; // 0xC
uint8_t unk_0D[3]; // 0xD
uint8_t timeslice_ended; // 0xD
uint8_t timer_pending; // 0xE
uint8_t unk_0F; // 0xF
// used in KeSaveFloatingPointState / its vmx counterpart
xe::be<uint32_t> thread_fpu_related; // 0x10
xe::be<uint32_t> thread_vmx_related; // 0x14
uint8_t current_irql; // 0x18
uint8_t unk_19[0x17]; // 0x19
xe::be<uint64_t> pcr_ptr; // 0x30
xe::be<uint32_t> thread_fpu_related; // 0x10
xe::be<uint32_t> thread_vmx_related; // 0x14
uint8_t current_irql; // 0x18
uint8_t background_scheduling_active; // 0x19
uint8_t background_scheduling_1A; // 0x1A
uint8_t background_scheduling_1B; // 0x1B
xe::be<uint32_t> timer_related; // 0x1C
uint8_t unk_20[0x10]; // 0x20
xe::be<uint64_t> pcr_ptr; // 0x30
// this seems to be just garbage data? we can stash a pointer to context here
// as a hack for now
@ -174,27 +213,54 @@ struct X_KPCR {
uint8_t unk_2AC[0x2C]; // 0x2AC
};
enum : uint16_t {
WAIT_ALL = 0,
WAIT_ANY = 1,
};
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ntos/ke_x/kwait_block.htm
// pretty much the vista KWAIT_BLOCK verbatim, except that sparebyte is gone
// and WaitType is 2 bytes instead of 1
struct X_KWAIT_BLOCK {
X_LIST_ENTRY wait_list_entry; // 0x0
TypedGuestPointer<X_KTHREAD> thread;
TypedGuestPointer<X_DISPATCH_HEADER> object;
TypedGuestPointer<X_KWAIT_BLOCK> next_wait_block;
// this isnt the official vista name, but i think its better.
// this value is what will be returned to the waiter if this particular wait
// is satisfied
xe::be<uint16_t> wait_result_xstatus;
// WAIT_ALL or WAIT_ANY
xe::be<uint16_t> wait_type;
};
static_assert_size(X_KWAIT_BLOCK, 0x18);
struct X_KTIMER {
X_DISPATCH_HEADER header; // 0x0
xe::be<uint64_t> due_time; // 0x10
X_LIST_ENTRY table_bucket_entry; // 0x18
TypedGuestPointer<XDPC> dpc; // 0x20
xe::be<uint32_t> period; // 0x24
};
static_assert_size(X_KTIMER, 0x28);
struct X_KTHREAD {
X_DISPATCH_HEADER header; // 0x0
xe::be<uint32_t> unk_10; // 0x10
xe::be<uint32_t> unk_14; // 0x14
uint8_t unk_18[0x28]; // 0x10
xe::be<uint32_t> unk_40; // 0x40
xe::be<uint32_t> unk_44; // 0x44
xe::be<uint32_t> unk_48; // 0x48
xe::be<uint32_t> unk_4C; // 0x4C
uint8_t unk_50[0x4]; // 0x50
xe::be<uint16_t> unk_54; // 0x54
xe::be<uint16_t> unk_56; // 0x56
uint8_t unk_58[0x4]; // 0x58
xe::be<uint32_t> stack_base; // 0x5C
xe::be<uint32_t> stack_limit; // 0x60
xe::be<uint32_t> stack_kernel; // 0x64
xe::be<uint32_t> tls_address; // 0x68
X_DISPATCH_HEADER header; // 0x0
xe::be<uint32_t> unk_10; // 0x10
xe::be<uint32_t> unk_14; // 0x14
X_KTIMER wait_timeout_timer; // 0x18
X_KWAIT_BLOCK wait_timeout_block; // 0x40
uint8_t unk_58[0x4]; // 0x58
xe::be<uint32_t> stack_base; // 0x5C
xe::be<uint32_t> stack_limit; // 0x60
xe::be<uint32_t> stack_kernel; // 0x64
xe::be<uint32_t> tls_address; // 0x68
// state = is thread running, suspended, etc
uint8_t thread_state; // 0x6C
// 0x70 = priority?
uint8_t unk_6D[0x3]; // 0x6D
uint8_t alerted[2]; // 0x6D
uint8_t alertable; // 0x6F
uint8_t priority; // 0x70
uint8_t fpu_exceptions_on; // 0x71
// these two process types both get set to the same thing, process_type is
@ -205,50 +271,56 @@ struct X_KTHREAD {
// apc_mode determines which list an apc goes into
util::X_TYPED_LIST<XAPC, offsetof(XAPC, list_entry)> apc_lists[2];
TypedGuestPointer<X_KPROCESS> process; // 0x84
uint8_t unk_88[0x3]; // 0x88
uint8_t may_queue_apcs; // 0x8B
X_KSPINLOCK apc_lock; // 0x8C
uint8_t unk_90[0xC]; // 0x90
xe::be<uint32_t> msr_mask; // 0x9C
uint8_t unk_A0[4]; // 0xA0
uint8_t unk_A4; // 0xA4
uint8_t unk_A5[0xB]; // 0xA5
int32_t apc_disable_count; // 0xB0
uint8_t unk_B4[4]; // 0xB4
uint8_t unk_B8; // 0xB8
uint8_t unk_B9; // 0xB9
uint8_t unk_BA; // 0xBA
uint8_t boost_disabled; // 0xBB
uint8_t suspend_count; // 0xBC
uint8_t unk_BD; // 0xBD
uint8_t terminated; // 0xBE
uint8_t current_cpu; // 0xBF
uint8_t executing_kernel_apc; // 0x88
// when context switch happens, this is copied into
// apc_software_interrupt_state for kpcr
uint8_t deferred_apc_software_interrupt_state; // 0x89
uint8_t user_apc_pending; // 0x8A
uint8_t may_queue_apcs; // 0x8B
X_KSPINLOCK apc_lock; // 0x8C
xe::be<uint32_t> num_context_switches_to; // 0x90
X_LIST_ENTRY ready_prcb_entry; // 0x94
xe::be<uint32_t> msr_mask; // 0x9C
xe::be<X_STATUS> wait_result; // 0xA0
uint8_t wait_irql; // 0xA4
uint8_t unk_A5[0xB]; // 0xA5
int32_t apc_disable_count; // 0xB0
xe::be<int32_t> quantum; // 0xB4
uint8_t unk_B8; // 0xB8
uint8_t unk_B9; // 0xB9
uint8_t unk_BA; // 0xBA
uint8_t boost_disabled; // 0xBB
uint8_t suspend_count; // 0xBC
uint8_t was_preempted; // 0xBD
uint8_t terminated; // 0xBE
uint8_t current_cpu; // 0xBF
// these two pointers point to KPRCBs, but seem to be rarely referenced, if at
// all
TypedGuestPointer<X_KPRCB> a_prcb_ptr; // 0xC0
TypedGuestPointer<X_KPRCB> another_prcb_ptr; // 0xC4
uint8_t unk_C8[8]; // 0xC8
uint8_t unk_C8; // 0xC8
uint8_t unk_C9; // 0xC9
uint8_t unk_CA; // 0xCA
uint8_t unk_CB; // 0xCB
X_KSPINLOCK timer_list_lock; // 0xCC
xe::be<uint32_t> stack_alloc_base; // 0xD0
// uint8_t unk_D4[0x5C]; // 0xD4
XAPC on_suspend; // 0xD4
X_KSEMAPHORE unk_FC; // 0xFC
XAPC on_suspend; // 0xD4
X_KSEMAPHORE suspend_sema; // 0xFC
// this is an entry in
X_LIST_ENTRY process_threads; // 0x110
xe::be<uint32_t> unk_118; // 0x118
xe::be<uint32_t> unk_11C; // 0x11C
xe::be<uint32_t> unk_120; // 0x120
xe::be<uint32_t> unk_124; // 0x124
xe::be<uint32_t> unk_128; // 0x128
xe::be<uint32_t> unk_12C; // 0x12C
xe::be<uint64_t> create_time; // 0x130
xe::be<uint64_t> exit_time; // 0x138
xe::be<uint32_t> exit_status; // 0x140
xe::be<uint32_t> unk_144; // 0x144
xe::be<uint32_t> unk_148; // 0x148
X_LIST_ENTRY process_threads; // 0x110
xe::be<uint32_t> unk_118; // 0x118
X_LIST_ENTRY queue_related; // 0x11C
xe::be<uint32_t> unk_124; // 0x124
xe::be<uint32_t> unk_128; // 0x128
xe::be<uint32_t> unk_12C; // 0x12C
xe::be<uint64_t> create_time; // 0x130
xe::be<uint64_t> exit_time; // 0x138
xe::be<uint32_t> exit_status; // 0x140
// tracks all pending timers that have apcs which target this thread
X_LIST_ENTRY timer_list; // 0x144
xe::be<uint32_t> thread_id; // 0x14C
xe::be<uint32_t> start_address; // 0x150
xe::be<uint32_t> unk_154; // 0x154
xe::be<uint32_t> unk_158; // 0x158
X_LIST_ENTRY unk_154; // 0x154
uint8_t unk_15C[0x4]; // 0x15C
xe::be<uint32_t> last_error; // 0x160
xe::be<uint32_t> fiber_ptr; // 0x164