Refactor runtime ownership and clean up warnings
This commit is contained in:
parent
f23a3b3add
commit
486b061558
628 changed files with 97954 additions and 90763 deletions
156
crates/rrt-hook/src/capture.rs
Normal file
156
crates/rrt-hook/src/capture.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rrt_model::finance::{
|
||||
AnnualFinancePolicy, BondPosition, CompanyFinanceState, FinanceOutcome, FinanceSnapshot,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FinanceLogPaths {
|
||||
pub snapshot_path: PathBuf,
|
||||
pub outcome_path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn sample_finance_snapshot() -> FinanceSnapshot {
|
||||
FinanceSnapshot {
|
||||
policy: AnnualFinancePolicy {
|
||||
dividends_allowed: false,
|
||||
..AnnualFinancePolicy::default()
|
||||
},
|
||||
company: CompanyFinanceState {
|
||||
current_cash: 100_000,
|
||||
support_adjusted_share_price: 27.5,
|
||||
book_value_per_share: 20.0,
|
||||
outstanding_share_count: 60_000,
|
||||
recent_net_profits: [40_000, 30_000, 20_000],
|
||||
recent_revenue_totals: [250_000, 240_000, 230_000],
|
||||
bonds: vec![
|
||||
BondPosition {
|
||||
principal: 150_000,
|
||||
coupon_rate: 0.12,
|
||||
years_remaining: 12,
|
||||
},
|
||||
BondPosition {
|
||||
principal: 10_000,
|
||||
coupon_rate: 0.10,
|
||||
years_remaining: 10,
|
||||
},
|
||||
],
|
||||
..CompanyFinanceState::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_finance_snapshot_bundle(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
snapshot: &FinanceSnapshot,
|
||||
) -> io::Result<FinanceLogPaths> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
|
||||
let outcome_path = base_dir.join(format!("rrt_finance_{stem}_outcome.json"));
|
||||
let outcome: FinanceOutcome = snapshot.evaluate();
|
||||
|
||||
let snapshot_json = serde_json::to_vec_pretty(snapshot)
|
||||
.map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
|
||||
let outcome_json = serde_json::to_vec_pretty(&outcome)
|
||||
.map_err(|err| io::Error::other(format!("serialize outcome: {err}")))?;
|
||||
|
||||
fs::write(&snapshot_path, snapshot_json)?;
|
||||
fs::write(&outcome_path, outcome_json)?;
|
||||
|
||||
Ok(FinanceLogPaths {
|
||||
snapshot_path,
|
||||
outcome_path,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_finance_snapshot_only(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
snapshot: &FinanceSnapshot,
|
||||
) -> io::Result<PathBuf> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
|
||||
let snapshot_json = serde_json::to_vec_pretty(snapshot)
|
||||
.map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
|
||||
fs::write(&snapshot_path, snapshot_json)?;
|
||||
|
||||
Ok(snapshot_path)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct IndexedCollectionProbeRow {
|
||||
pub entry_id: usize,
|
||||
pub live: bool,
|
||||
pub resolved_ptr: usize,
|
||||
pub active_flag: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct IndexedCollectionProbe {
|
||||
pub collection_addr: usize,
|
||||
pub flat_payload: bool,
|
||||
pub stride: u32,
|
||||
pub id_bound: i32,
|
||||
pub payload_ptr: usize,
|
||||
pub tombstone_ptr: usize,
|
||||
pub first_rows: Vec<IndexedCollectionProbeRow>,
|
||||
}
|
||||
|
||||
pub fn write_indexed_collection_probe(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
probe: &IndexedCollectionProbe,
|
||||
) -> io::Result<PathBuf> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let path = base_dir.join(format!("rrt_finance_{stem}_collection_probe.json"));
|
||||
let json = serde_json::to_vec_pretty(probe)
|
||||
.map_err(|err| io::Error::other(format!("serialize collection probe: {err}")))?;
|
||||
fs::write(&path, json)?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CargoCollectionProbeRow {
|
||||
pub entry_id: usize,
|
||||
pub live: bool,
|
||||
pub resolved_ptr: usize,
|
||||
pub stem: Option<String>,
|
||||
pub route_style_byte: Option<u8>,
|
||||
pub subtype_byte: Option<u8>,
|
||||
pub class_marker: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CargoCollectionProbe {
|
||||
pub collection_addr: usize,
|
||||
pub flat_payload: bool,
|
||||
pub stride: u32,
|
||||
pub id_bound: i32,
|
||||
pub payload_ptr: usize,
|
||||
pub tombstone_ptr: usize,
|
||||
pub live_entry_count: usize,
|
||||
pub rows: Vec<CargoCollectionProbeRow>,
|
||||
}
|
||||
|
||||
pub fn write_cargo_collection_probe(
|
||||
base_dir: &Path,
|
||||
stem: &str,
|
||||
probe: &CargoCollectionProbe,
|
||||
) -> io::Result<PathBuf> {
|
||||
fs::create_dir_all(base_dir)?;
|
||||
|
||||
let path = base_dir.join(format!("rrt_cargo_{stem}_collection_probe.json"));
|
||||
let json = serde_json::to_vec_pretty(probe)
|
||||
.map_err(|err| io::Error::other(format!("serialize cargo collection probe: {err}")))?;
|
||||
fs::write(&path, json)?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
377
crates/rrt-hook/src/windows/auto_load.rs
Normal file
377
crates/rrt-hook/src/windows/auto_load.rs
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) fn maybe_install_auto_load_hook() {
|
||||
let save_stem = match env::var("RRT_AUTO_LOAD_SAVE") {
|
||||
Ok(value) if !value.trim().is_empty() => value,
|
||||
_ => return,
|
||||
};
|
||||
let _ = AUTO_LOAD_SAVE_STEM.set(save_stem);
|
||||
if AUTO_LOAD_HOOK_INSTALLED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
append_log_message(AUTO_LOAD_STARTED_MESSAGE);
|
||||
AUTO_LOAD_THREAD_STARTED.store(true, Ordering::Release);
|
||||
if unsafe { install_shell_state_service_hook() } {
|
||||
append_log_message(AUTO_LOAD_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_profile_startup_dispatch_hook() } {
|
||||
append_log_message(AUTO_LOAD_PROFILE_DISPATCH_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_runtime_reset_hook() } {
|
||||
append_log_message(AUTO_LOAD_RUNTIME_RESET_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_allocator_hook() } {
|
||||
append_log_message(AUTO_LOAD_ALLOCATOR_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_load_screen_scalar_hook() } {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_SCALAR_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_load_screen_construct_hook() } {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_load_screen_message_hook() } {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_MESSAGE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_runtime_prime_hook() } {
|
||||
append_log_message(AUTO_LOAD_RUNTIME_PRIME_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_frame_cycle_hook() } {
|
||||
append_log_message(AUTO_LOAD_FRAME_CYCLE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_object_service_hook() } {
|
||||
append_log_message(AUTO_LOAD_OBJECT_SERVICE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_child_service_hook() } {
|
||||
append_log_message(AUTO_LOAD_CHILD_SERVICE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_publish_hook() } {
|
||||
append_log_message(AUTO_LOAD_PUBLISH_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_unpublish_hook() } {
|
||||
append_log_message(AUTO_LOAD_UNPUBLISH_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_object_teardown_hook() } {
|
||||
append_log_message(AUTO_LOAD_OBJECT_TEARDOWN_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_object_range_remove_hook() } {
|
||||
append_log_message(AUTO_LOAD_OBJECT_RANGE_REMOVE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_remove_node_hook() } {
|
||||
append_log_message(AUTO_LOAD_REMOVE_NODE_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_shell_node_vcall_hook() } {
|
||||
append_log_message(AUTO_LOAD_NODE_VCALL_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
if unsafe { install_mode2_teardown_hook() } {
|
||||
append_log_message(AUTO_LOAD_MODE2_TEARDOWN_HOOK_INSTALLED_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn run_auto_load_worker() {
|
||||
append_log_message(AUTO_LOAD_CALLING_MESSAGE);
|
||||
let staged = unsafe { invoke_manual_load_branch() };
|
||||
if staged {
|
||||
append_log_message(AUTO_LOAD_TRIGGERED_MESSAGE);
|
||||
append_log_message(AUTO_LOAD_SUCCESS_MESSAGE);
|
||||
} else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
}
|
||||
AUTO_LOAD_IN_PROGRESS.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
pub(super) unsafe fn invoke_manual_load_branch() -> bool {
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if shell_state.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let shell_transition_mode: ShellTransitionModeFn =
|
||||
unsafe { mem::transmute(SHELL_TRANSITION_MODE_ADDR) };
|
||||
|
||||
AUTO_LOAD_ALLOCATOR_WINDOW_LOG_COUNT.store(0, Ordering::Release);
|
||||
AUTO_LOAD_ALLOCATOR_WINDOW_ACTIVE.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_OWNER_ENTRY_MESSAGE);
|
||||
let _ = unsafe { shell_transition_mode(shell_state, SHELL_MODE_STARTUP_LOAD_DISPATCH, 0) };
|
||||
AUTO_LOAD_ALLOCATOR_WINDOW_ACTIVE.store(false, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_OWNER_RETURNED_MESSAGE);
|
||||
log_post_transition_state();
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn stage_manual_load_request(save_stem: &str) -> bool {
|
||||
if save_stem.is_empty() || save_stem.as_bytes().contains(&0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let runtime_profile = unsafe { read_ptr(RUNTIME_PROFILE_PTR_ADDR as *const u8) };
|
||||
if runtime_profile.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let path_seed = unsafe { runtime_profile.add(RUNTIME_PROFILE_MANUAL_LOAD_PATH_OFFSET) };
|
||||
if unsafe { write_c_string(path_seed, 260, save_stem.as_bytes()) }.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ptr::write_unaligned(
|
||||
runtime_profile
|
||||
.add(RUNTIME_PROFILE_STARTUP_SELECTOR_OFFSET)
|
||||
.cast::<u8>(),
|
||||
STARTUP_SELECTOR_SCENARIO_LOAD,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
ptr::write_unaligned(
|
||||
runtime_profile
|
||||
.add(RUNTIME_PROFILE_PENDING_LOAD_BYTE_OFFSET)
|
||||
.cast::<u8>(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
log_auto_load_launch_state(runtime_profile);
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn write_c_string(
|
||||
destination: *mut u8,
|
||||
capacity: usize,
|
||||
bytes: &[u8],
|
||||
) -> Option<()> {
|
||||
if bytes.len() + 1 > capacity {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe { ptr::write_bytes(destination, 0, capacity) };
|
||||
unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), destination, bytes.len()) };
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub(super) unsafe fn runtime_saved_world_restore_gate_mask() -> u32 {
|
||||
let mut mask = 0_u32;
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if !shell_state.is_null() {
|
||||
mask |= 0x1;
|
||||
}
|
||||
let shell_controller = unsafe { read_ptr(SHELL_CONTROLLER_PTR_ADDR as *const u8) };
|
||||
if !shell_controller.is_null() {
|
||||
mask |= 0x2;
|
||||
}
|
||||
let active_mode = unsafe { resolve_active_mode_ptr() };
|
||||
if !active_mode.is_null() {
|
||||
mask |= 0x4;
|
||||
}
|
||||
mask
|
||||
}
|
||||
|
||||
pub(super) unsafe fn current_mode_id() -> u32 {
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if shell_state.is_null() {
|
||||
return 0;
|
||||
}
|
||||
unsafe { read_u32(shell_state.add(SHELL_STATE_ACTIVE_MODE_OFFSET)) }
|
||||
}
|
||||
|
||||
pub(super) fn auto_load_ready_polls() -> u32 {
|
||||
env::var("RRT_AUTO_LOAD_READY_POLLS")
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u32>().ok())
|
||||
.filter(|value| *value > 0)
|
||||
.unwrap_or(AUTO_LOAD_READY_POLLS)
|
||||
}
|
||||
|
||||
pub(super) fn auto_load_defer_polls() -> u32 {
|
||||
env::var("RRT_AUTO_LOAD_DEFER_POLLS")
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u32>().ok())
|
||||
.unwrap_or(AUTO_LOAD_DEFER_POLLS)
|
||||
}
|
||||
|
||||
pub(super) fn maybe_service_auto_load_on_main_thread() {
|
||||
if !AUTO_LOAD_HOOK_INSTALLED.load(Ordering::Acquire)
|
||||
|| AUTO_LOAD_ATTEMPTED.load(Ordering::Acquire)
|
||||
|| AUTO_LOAD_IN_PROGRESS.load(Ordering::Acquire)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let gate_mask = unsafe { runtime_saved_world_restore_gate_mask() };
|
||||
let last_gate_mask = AUTO_LOAD_LAST_GATE_MASK.swap(gate_mask, Ordering::AcqRel);
|
||||
if gate_mask != last_gate_mask {
|
||||
log_auto_load_gate_mask(gate_mask);
|
||||
}
|
||||
|
||||
let mode_id = unsafe { current_mode_id() };
|
||||
let ready = gate_mask == 0x7 && mode_id == 2;
|
||||
let ready_count = if ready {
|
||||
AUTO_LOAD_READY_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
AUTO_LOAD_READY_COUNT.store(0, Ordering::Release);
|
||||
AUTO_LOAD_DEFERRED.store(false, Ordering::Release);
|
||||
0
|
||||
};
|
||||
if ready {
|
||||
append_log_message(AUTO_LOAD_READY_COUNT_MESSAGE);
|
||||
log_auto_load_ready_count(ready_count, gate_mask, mode_id);
|
||||
}
|
||||
|
||||
let ready_polls = auto_load_ready_polls();
|
||||
if ready_count < ready_polls {
|
||||
return;
|
||||
}
|
||||
|
||||
if !AUTO_LOAD_DEFERRED.load(Ordering::Acquire) {
|
||||
AUTO_LOAD_DEFERRED.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_READY_MESSAGE);
|
||||
append_log_message(AUTO_LOAD_DEFERRED_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ready_count < ready_polls.saturating_add(auto_load_defer_polls()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if AUTO_LOAD_ATTEMPTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !AUTO_LOAD_TRANSITION_ARMED.load(Ordering::Acquire) {
|
||||
let Some(save_stem) = AUTO_LOAD_SAVE_STEM.get() else {
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
AUTO_LOAD_ATTEMPTED.store(true, Ordering::Release);
|
||||
return;
|
||||
};
|
||||
if unsafe { stage_manual_load_request(save_stem) } {
|
||||
AUTO_LOAD_TRANSITION_ARMED.store(true, Ordering::Release);
|
||||
AUTO_LOAD_ARMED_TICK_COUNT.store(0, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_STAGED_MESSAGE);
|
||||
log_auto_load_armed_tick();
|
||||
AUTO_LOAD_ATTEMPTED.store(true, Ordering::Release);
|
||||
AUTO_LOAD_IN_PROGRESS.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_ARMED_TICK_MESSAGE);
|
||||
append_log_message(AUTO_LOAD_READY_MESSAGE);
|
||||
run_auto_load_worker();
|
||||
return;
|
||||
}
|
||||
append_log_message(AUTO_LOAD_FAILURE_MESSAGE);
|
||||
AUTO_LOAD_ATTEMPTED.store(true, Ordering::Release);
|
||||
return;
|
||||
}
|
||||
|
||||
AUTO_LOAD_IN_PROGRESS.store(true, Ordering::Release);
|
||||
append_log_message(AUTO_LOAD_READY_MESSAGE);
|
||||
run_auto_load_worker();
|
||||
}
|
||||
|
||||
pub(super) fn log_auto_load_gate_mask(mask: u32) {
|
||||
let mut line = String::from("rrt-hook: auto load gate mask ");
|
||||
let global_active_mode = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as usize;
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
let mode_id = if shell_state.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { read_u32(shell_state.add(SHELL_STATE_ACTIVE_MODE_OFFSET)) as usize }
|
||||
};
|
||||
let field_active_mode_object = if shell_state.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { read_ptr(shell_state.add(SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET)) as usize }
|
||||
};
|
||||
let startup_selector = unsafe {
|
||||
let runtime_profile = read_ptr(RUNTIME_PROFILE_PTR_ADDR as *const u8);
|
||||
if runtime_profile.is_null() {
|
||||
0
|
||||
} else {
|
||||
read_u8(runtime_profile.add(RUNTIME_PROFILE_STARTUP_SELECTOR_OFFSET)) as usize
|
||||
}
|
||||
};
|
||||
let _ = write!(
|
||||
&mut line,
|
||||
"0x{mask:01x} shell_state={} shell_controller={} active_mode={} global_active_mode=0x{global_active_mode:08x} mode_id=0x{mode_id:08x} startup_selector=0x{startup_selector:08x} field_active_mode_object=0x{field_active_mode_object:08x}\n",
|
||||
(mask & 0x1) != 0,
|
||||
(mask & 0x2) != 0,
|
||||
(mask & 0x4) != 0,
|
||||
);
|
||||
append_log_line(&line);
|
||||
}
|
||||
|
||||
pub(super) fn log_auto_load_armed_tick() {
|
||||
let tick_count = AUTO_LOAD_ARMED_TICK_COUNT.fetch_add(1, Ordering::AcqRel) + 1;
|
||||
let gate_mask = unsafe { runtime_saved_world_restore_gate_mask() };
|
||||
let global_active_mode = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as usize;
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
let mode_id = if shell_state.is_null() {
|
||||
0
|
||||
} else {
|
||||
unsafe { read_u32(shell_state.add(SHELL_STATE_ACTIVE_MODE_OFFSET)) as usize }
|
||||
};
|
||||
let startup_selector = unsafe {
|
||||
let runtime_profile = read_ptr(RUNTIME_PROFILE_PTR_ADDR as *const u8);
|
||||
if runtime_profile.is_null() {
|
||||
0
|
||||
} else {
|
||||
read_u8(runtime_profile.add(RUNTIME_PROFILE_STARTUP_SELECTOR_OFFSET)) as usize
|
||||
}
|
||||
};
|
||||
let mut line = String::from("rrt-hook: auto load armed tick ");
|
||||
let _ = write!(
|
||||
&mut line,
|
||||
"count=0x{tick_count:08x} gate_mask=0x{gate_mask:01x} mode_id=0x{mode_id:08x} startup_selector=0x{startup_selector:08x} global_active_mode=0x{global_active_mode:08x}\n",
|
||||
);
|
||||
append_log_line(&line);
|
||||
}
|
||||
|
||||
pub(super) fn log_auto_load_ready_count(count: u32, gate_mask: u32, mode_id: u32) {
|
||||
let startup_selector = unsafe {
|
||||
let runtime_profile = read_ptr(RUNTIME_PROFILE_PTR_ADDR as *const u8);
|
||||
if runtime_profile.is_null() {
|
||||
0
|
||||
} else {
|
||||
read_u8(runtime_profile.add(RUNTIME_PROFILE_STARTUP_SELECTOR_OFFSET)) as usize
|
||||
}
|
||||
};
|
||||
let mut line = String::from("rrt-hook: auto load ready count ");
|
||||
let _ = write!(
|
||||
&mut line,
|
||||
"count=0x{count:08x} gate_mask=0x{gate_mask:01x} mode_id=0x{mode_id:08x} startup_selector=0x{startup_selector:08x}\n",
|
||||
);
|
||||
append_log_line(&line);
|
||||
}
|
||||
333
crates/rrt-hook/src/windows/capture.rs
Normal file
333
crates/rrt-hook/src/windows/capture.rs
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) fn maybe_emit_finance_template_bundle() {
|
||||
if env::var_os("RRT_WRITE_FINANCE_TEMPLATE").is_none() {
|
||||
return;
|
||||
}
|
||||
if FINANCE_TEMPLATE_EMITTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _ = write_finance_snapshot_bundle(&base_dir, "attach_template", &sample_finance_snapshot());
|
||||
}
|
||||
|
||||
pub(super) fn maybe_start_finance_capture_thread() {
|
||||
if env::var_os("RRT_WRITE_FINANCE_CAPTURE").is_none() {
|
||||
return;
|
||||
}
|
||||
if FINANCE_CAPTURE_STARTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
append_log_message(FINANCE_CAPTURE_STARTED_MESSAGE);
|
||||
let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _ = thread::Builder::new()
|
||||
.name("rrt-finance-capture".to_string())
|
||||
.spawn(move || {
|
||||
for _ in 0..MAX_CAPTURE_POLL_ATTEMPTS {
|
||||
if !FINANCE_COLLECTION_PROBE_WRITTEN.load(Ordering::Acquire) {
|
||||
if let Some(probe) = unsafe { capture_company_collection_probe() } {
|
||||
if write_indexed_collection_probe(&base_dir, "attach_probe", &probe).is_ok()
|
||||
{
|
||||
FINANCE_COLLECTION_PROBE_WRITTEN.store(true, Ordering::Release);
|
||||
append_log_message(FINANCE_CAPTURE_PROBE_DUMP_WRITTEN_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(snapshot) = unsafe { try_capture_probe_snapshot() } {
|
||||
append_log_message(FINANCE_CAPTURE_COMPANY_RESOLVED_MESSAGE);
|
||||
if write_finance_snapshot_only(&base_dir, "attach_probe", &snapshot).is_ok() {
|
||||
append_log_message(FINANCE_CAPTURE_PROBE_WRITTEN_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
thread::sleep(CAPTURE_POLL_INTERVAL);
|
||||
}
|
||||
|
||||
append_log_message(FINANCE_CAPTURE_TIMEOUT_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn maybe_start_cargo_capture_thread() {
|
||||
if env::var_os("RRT_WRITE_CARGO_CAPTURE").is_none() {
|
||||
return;
|
||||
}
|
||||
if CARGO_CAPTURE_STARTED.swap(true, Ordering::AcqRel) {
|
||||
return;
|
||||
}
|
||||
|
||||
append_log_message(CARGO_CAPTURE_STARTED_MESSAGE);
|
||||
let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let _ = thread::Builder::new()
|
||||
.name("rrt-cargo-capture".to_string())
|
||||
.spawn(move || {
|
||||
let mut last_probe: Option<CargoCollectionProbe> = None;
|
||||
for _ in 0..MAX_CAPTURE_POLL_ATTEMPTS {
|
||||
if let Some(probe) = unsafe { capture_cargo_collection_probe() } {
|
||||
last_probe = Some(probe.clone());
|
||||
if probe.live_entry_count > 0
|
||||
&& write_cargo_collection_probe(&base_dir, "attach_probe", &probe).is_ok()
|
||||
{
|
||||
append_log_message(CARGO_CAPTURE_WRITTEN_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
thread::sleep(CAPTURE_POLL_INTERVAL);
|
||||
}
|
||||
|
||||
if let Some(probe) = last_probe {
|
||||
let _ = write_cargo_collection_probe(&base_dir, "attach_probe_timeout", &probe);
|
||||
}
|
||||
append_log_message(CARGO_CAPTURE_TIMEOUT_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) unsafe fn try_capture_probe_snapshot() -> Option<FinanceSnapshot> {
|
||||
append_log_message(FINANCE_CAPTURE_SCAN_MESSAGE);
|
||||
let company = unsafe { resolve_first_active_company()? };
|
||||
Some(unsafe { capture_probe_snapshot_from_company(company) })
|
||||
}
|
||||
|
||||
pub(super) unsafe fn resolve_first_active_company() -> Option<*mut u8> {
|
||||
let collection = COMPANY_COLLECTION_ADDR as *const u8;
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if id_bound <= 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
for entry_id in 1..=id_bound as usize {
|
||||
if unsafe { indexed_collection_entry_id_is_live(collection, entry_id) } {
|
||||
let company =
|
||||
unsafe { indexed_collection_resolve_live_entry_by_id(collection, entry_id) };
|
||||
if !company.is_null() && unsafe { read_u8(company.add(COMPANY_ACTIVE_OFFSET)) != 0 } {
|
||||
return Some(company);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) unsafe fn capture_company_collection_probe() -> Option<IndexedCollectionProbe> {
|
||||
let collection = COMPANY_COLLECTION_ADDR as *const u8;
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if id_bound <= 0 {
|
||||
return Some(IndexedCollectionProbe {
|
||||
collection_addr: COMPANY_COLLECTION_ADDR,
|
||||
flat_payload: unsafe {
|
||||
read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0
|
||||
},
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize
|
||||
},
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
first_rows: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut first_rows = Vec::new();
|
||||
let sample_bound = (id_bound as usize).min(8);
|
||||
for entry_id in 1..=sample_bound {
|
||||
let live = unsafe { indexed_collection_entry_id_is_live(collection, entry_id) };
|
||||
let resolved_ptr =
|
||||
unsafe { indexed_collection_resolve_live_entry_by_id(collection, entry_id) as usize };
|
||||
let active_flag = if resolved_ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { read_u8((resolved_ptr as *const u8).add(COMPANY_ACTIVE_OFFSET)) })
|
||||
};
|
||||
first_rows.push(IndexedCollectionProbeRow {
|
||||
entry_id,
|
||||
live,
|
||||
resolved_ptr,
|
||||
active_flag,
|
||||
});
|
||||
}
|
||||
|
||||
Some(IndexedCollectionProbe {
|
||||
collection_addr: COMPANY_COLLECTION_ADDR,
|
||||
flat_payload: unsafe { read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 },
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize
|
||||
},
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
first_rows,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) unsafe fn capture_cargo_collection_probe() -> Option<CargoCollectionProbe> {
|
||||
let collection = CARGO_COLLECTION_ADDR as *const u8;
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if id_bound <= 0 {
|
||||
return Some(CargoCollectionProbe {
|
||||
collection_addr: CARGO_COLLECTION_ADDR,
|
||||
flat_payload: unsafe {
|
||||
read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0
|
||||
},
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize
|
||||
},
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
live_entry_count: 0,
|
||||
rows: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut live_entry_count = 0_usize;
|
||||
let mut rows = Vec::with_capacity(id_bound as usize);
|
||||
for entry_id in 1..=id_bound as usize {
|
||||
let live = unsafe { indexed_collection_entry_id_is_live(collection, entry_id) };
|
||||
let resolved_ptr =
|
||||
unsafe { indexed_collection_resolve_live_entry_by_id(collection, entry_id) as usize };
|
||||
if live && resolved_ptr != 0 {
|
||||
live_entry_count += 1;
|
||||
}
|
||||
let stem = if resolved_ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { read_c_string((resolved_ptr as *const u8).add(CARGO_STEM_OFFSET), 0x1e) })
|
||||
};
|
||||
let route_style_byte = if resolved_ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { read_u8((resolved_ptr as *const u8).add(CARGO_ROUTE_STYLE_OFFSET)) })
|
||||
};
|
||||
let subtype_byte = if resolved_ptr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { read_u8((resolved_ptr as *const u8).add(CARGO_SUBTYPE_OFFSET)) })
|
||||
};
|
||||
let class_marker = if live {
|
||||
Some(unsafe {
|
||||
read_u32(collection.add(CARGO_COLLECTION_CLASS_MARKER_BASE_OFFSET + entry_id * 4))
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
rows.push(CargoCollectionProbeRow {
|
||||
entry_id,
|
||||
live,
|
||||
resolved_ptr,
|
||||
stem,
|
||||
route_style_byte,
|
||||
subtype_byte,
|
||||
class_marker,
|
||||
});
|
||||
}
|
||||
|
||||
Some(CargoCollectionProbe {
|
||||
collection_addr: CARGO_COLLECTION_ADDR,
|
||||
flat_payload: unsafe { read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 },
|
||||
stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) },
|
||||
id_bound,
|
||||
payload_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize
|
||||
},
|
||||
tombstone_ptr: unsafe {
|
||||
read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize
|
||||
},
|
||||
live_entry_count,
|
||||
rows,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) unsafe fn capture_probe_snapshot_from_company(company: *mut u8) -> FinanceSnapshot {
|
||||
let scenario = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as *const u8;
|
||||
let current_year = unsafe { read_u16(scenario.add(SCENARIO_CURRENT_YEAR_OFFSET)) };
|
||||
let founding_year = unsafe { read_u16(company.add(COMPANY_FOUNDING_YEAR_OFFSET)) };
|
||||
let last_bankruptcy_year =
|
||||
unsafe { read_u16(company.add(COMPANY_LAST_BANKRUPTCY_YEAR_OFFSET)) };
|
||||
let outstanding_share_count =
|
||||
unsafe { read_u32(company.add(COMPANY_OUTSTANDING_SHARES_OFFSET)) };
|
||||
let bonds = unsafe { capture_bonds(company, current_year) };
|
||||
let company_value = unsafe { read_u32(company.add(COMPANY_COMPANY_VALUE_OFFSET)) as i64 };
|
||||
let growth_setting = unsafe {
|
||||
growth_setting_from_raw(read_u8(
|
||||
scenario.add(SCENARIO_BUILDING_DENSITY_GROWTH_OFFSET),
|
||||
))
|
||||
};
|
||||
|
||||
FinanceSnapshot {
|
||||
policy: AnnualFinancePolicy {
|
||||
annual_mode: 0x0c,
|
||||
bankruptcy_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_BANKRUPTCY_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
bond_issuance_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_BOND_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
stock_actions_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_STOCK_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
dividends_allowed: unsafe {
|
||||
read_u8(scenario.add(SCENARIO_DIVIDEND_TOGGLE_OFFSET)) == 0
|
||||
},
|
||||
growth_setting,
|
||||
..AnnualFinancePolicy::default()
|
||||
},
|
||||
company: CompanyFinanceState {
|
||||
active: unsafe { read_u8(company.add(COMPANY_ACTIVE_OFFSET)) != 0 },
|
||||
years_since_founding: year_delta(current_year, founding_year),
|
||||
years_since_last_bankruptcy: year_delta(current_year, last_bankruptcy_year),
|
||||
current_company_value: company_value,
|
||||
outstanding_share_count,
|
||||
city_connection_bonus_latch: unsafe {
|
||||
read_u8(company.add(COMPANY_CITY_CONNECTION_LATCH_OFFSET)) != 0
|
||||
},
|
||||
linked_transit_service_latch: unsafe {
|
||||
read_u8(company.add(COMPANY_LINKED_TRANSIT_LATCH_OFFSET)) != 0
|
||||
},
|
||||
chairman_buyback_factor: None,
|
||||
bonds,
|
||||
..CompanyFinanceState::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn capture_bonds(company: *mut u8, current_year: u16) -> Vec<BondPosition> {
|
||||
let bond_count = unsafe { read_u8(company.add(COMPANY_BOND_COUNT_OFFSET)) as usize };
|
||||
let table = unsafe { company.add(COMPANY_BOND_TABLE_OFFSET) };
|
||||
let mut bonds = Vec::with_capacity(bond_count);
|
||||
|
||||
for index in 0..bond_count {
|
||||
let slot = unsafe { table.add(index * 12) };
|
||||
let principal = unsafe { read_i32(slot) } as i64;
|
||||
let maturity_year = unsafe { read_u32(slot.add(4)) };
|
||||
let coupon_rate = unsafe { read_f32(slot.add(8)) } as f64;
|
||||
|
||||
bonds.push(BondPosition {
|
||||
principal,
|
||||
coupon_rate,
|
||||
years_remaining: maturity_year
|
||||
.saturating_sub(current_year as u32)
|
||||
.min(u8::MAX as u32) as u8,
|
||||
});
|
||||
}
|
||||
|
||||
bonds
|
||||
}
|
||||
|
||||
pub(super) fn growth_setting_from_raw(raw: u8) -> GrowthSetting {
|
||||
match raw {
|
||||
1 => GrowthSetting::ExpansionBias,
|
||||
2 => GrowthSetting::DividendSuppressed,
|
||||
_ => GrowthSetting::Neutral,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn year_delta(current_year: u16, past_year: u16) -> u8 {
|
||||
current_year.saturating_sub(past_year).min(u8::MAX as u16) as u8
|
||||
}
|
||||
221
crates/rrt-hook/src/windows/constants.rs
Normal file
221
crates/rrt-hook/src/windows/constants.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) const DLL_PROCESS_ATTACH: u32 = 1;
|
||||
pub(super) const E_FAIL: i32 = 0x8000_4005_u32 as i32;
|
||||
pub(super) const FILE_APPEND_DATA: u32 = 0x0000_0004;
|
||||
pub(super) const FILE_SHARE_READ: u32 = 0x0000_0001;
|
||||
pub(super) const OPEN_ALWAYS: u32 = 4;
|
||||
pub(super) const FILE_ATTRIBUTE_NORMAL: u32 = 0x0000_0080;
|
||||
pub(super) const INVALID_HANDLE_VALUE: isize = -1;
|
||||
pub(super) const FILE_END: u32 = 2;
|
||||
|
||||
pub(super) static LOG_PATH: &[u8] = b"rrt_hook_attach.log\0";
|
||||
pub(super) static ATTACH_MESSAGE: &[u8] = b"rrt-hook: process attach\n";
|
||||
pub(super) static FINANCE_CAPTURE_STARTED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture thread started\n";
|
||||
pub(super) static FINANCE_CAPTURE_SCAN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture raw collection scan\n";
|
||||
pub(super) static FINANCE_CAPTURE_PROBE_DUMP_WRITTEN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance collection probe written\n";
|
||||
pub(super) static FINANCE_CAPTURE_COMPANY_RESOLVED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance capture company resolved\n";
|
||||
pub(super) static FINANCE_CAPTURE_PROBE_WRITTEN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: finance probe snapshot written\n";
|
||||
pub(super) static FINANCE_CAPTURE_TIMEOUT_MESSAGE: &[u8] = b"rrt-hook: finance capture timed out\n";
|
||||
pub(super) static CARGO_CAPTURE_STARTED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: cargo capture thread started\n";
|
||||
pub(super) static CARGO_CAPTURE_WRITTEN_MESSAGE: &[u8] =
|
||||
b"rrt-hook: cargo collection probe written\n";
|
||||
pub(super) static CARGO_CAPTURE_TIMEOUT_MESSAGE: &[u8] = b"rrt-hook: cargo capture timed out\n";
|
||||
pub(super) static AUTO_LOAD_STARTED_MESSAGE: &[u8] = b"rrt-hook: auto load hook armed\n";
|
||||
pub(super) static AUTO_LOAD_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell-state hook installed\n";
|
||||
pub(super) static AUTO_LOAD_PROFILE_DISPATCH_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load startup-dispatch hook installed\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_RESET_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load runtime-reset hook installed\n";
|
||||
pub(super) static AUTO_LOAD_ALLOCATOR_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load allocator hook installed\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_SCALAR_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen scalar hook installed\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen hook installed\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_MESSAGE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen message hook installed\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_PRIME_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell runtime-prime hook installed\n";
|
||||
pub(super) static AUTO_LOAD_FRAME_CYCLE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell frame-cycle hook installed\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_SERVICE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object-service hook installed\n";
|
||||
pub(super) static AUTO_LOAD_CHILD_SERVICE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell child-service hook installed\n";
|
||||
pub(super) static AUTO_LOAD_PUBLISH_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell publish hook installed\n";
|
||||
pub(super) static AUTO_LOAD_UNPUBLISH_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell unpublish hook installed\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_TEARDOWN_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object teardown hook installed\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_RANGE_REMOVE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object range-remove hook installed\n";
|
||||
pub(super) static AUTO_LOAD_REMOVE_NODE_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell remove-node hook installed\n";
|
||||
pub(super) static AUTO_LOAD_NODE_VCALL_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell node-vcall hook installed\n";
|
||||
pub(super) static AUTO_LOAD_MODE2_TEARDOWN_HOOK_INSTALLED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load mode2 teardown hook installed\n";
|
||||
pub(super) static AUTO_LOAD_READY_MESSAGE: &[u8] = b"rrt-hook: auto load ready gate passed\n";
|
||||
pub(super) static AUTO_LOAD_DEFERRED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load restore deferred to later service turn\n";
|
||||
pub(super) static AUTO_LOAD_CALLING_MESSAGE: &[u8] = b"rrt-hook: auto load restore calling\n";
|
||||
pub(super) static AUTO_LOAD_STAGED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load restore staged for later transition\n";
|
||||
pub(super) static AUTO_LOAD_READY_COUNT_MESSAGE: &[u8] = b"rrt-hook: auto load ready count\n";
|
||||
pub(super) static AUTO_LOAD_ARMED_TICK_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load armed transition tick\n";
|
||||
pub(super) static AUTO_LOAD_OWNER_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell transition entering\n";
|
||||
pub(super) static AUTO_LOAD_OWNER_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell transition returned\n";
|
||||
pub(super) static AUTO_LOAD_PROFILE_DISPATCH_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load startup dispatch entering\n";
|
||||
pub(super) static AUTO_LOAD_PROFILE_DISPATCH_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load startup dispatch returned\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_RESET_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load runtime reset entering\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_RESET_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load runtime reset returned\n";
|
||||
pub(super) static AUTO_LOAD_ALLOCATOR_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load allocator entering\n";
|
||||
pub(super) static AUTO_LOAD_ALLOCATOR_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load allocator returned\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_SCALAR_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen scalar entering\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_SCALAR_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen scalar returned\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen construct entering\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen construct returned\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_MESSAGE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen message entering\n";
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_MESSAGE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load load-screen message returned\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_PRIME_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell runtime-prime entering\n";
|
||||
pub(super) static AUTO_LOAD_RUNTIME_PRIME_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell runtime-prime returned\n";
|
||||
pub(super) static AUTO_LOAD_FRAME_CYCLE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell frame-cycle entering\n";
|
||||
pub(super) static AUTO_LOAD_FRAME_CYCLE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell frame-cycle returned\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_SERVICE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object-service entering\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_SERVICE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object-service returned\n";
|
||||
pub(super) static AUTO_LOAD_CHILD_SERVICE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell child-service entering\n";
|
||||
pub(super) static AUTO_LOAD_CHILD_SERVICE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell child-service returned\n";
|
||||
pub(super) static AUTO_LOAD_PUBLISH_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell publish entering\n";
|
||||
pub(super) static AUTO_LOAD_PUBLISH_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell publish returned\n";
|
||||
pub(super) static AUTO_LOAD_UNPUBLISH_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell unpublish entering\n";
|
||||
pub(super) static AUTO_LOAD_UNPUBLISH_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell unpublish returned\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_TEARDOWN_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object teardown entering\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_TEARDOWN_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object teardown returned\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_RANGE_REMOVE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object range-remove entering\n";
|
||||
pub(super) static AUTO_LOAD_OBJECT_RANGE_REMOVE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell object range-remove returned\n";
|
||||
pub(super) static AUTO_LOAD_REMOVE_NODE_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell remove-node entering\n";
|
||||
pub(super) static AUTO_LOAD_REMOVE_NODE_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell remove-node returned\n";
|
||||
pub(super) static AUTO_LOAD_NODE_VCALL_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell node-vcall entering\n";
|
||||
pub(super) static AUTO_LOAD_NODE_VCALL_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load shell node-vcall returned\n";
|
||||
pub(super) static AUTO_LOAD_MODE2_TEARDOWN_ENTRY_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load mode2 teardown entering\n";
|
||||
pub(super) static AUTO_LOAD_MODE2_TEARDOWN_RETURNED_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load mode2 teardown returned\n";
|
||||
pub(super) static AUTO_LOAD_TRIGGERED_MESSAGE: &[u8] = b"rrt-hook: auto load restore invoked\n";
|
||||
pub(super) static AUTO_LOAD_SUCCESS_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load request reported success\n";
|
||||
pub(super) static AUTO_LOAD_FAILURE_MESSAGE: &[u8] =
|
||||
b"rrt-hook: auto load request reported failure\n";
|
||||
pub(super) static DEBUG_MESSAGE: &[u8] = b"rrt-hook: DllMain process attach\0";
|
||||
pub(super) static DIRECT_INPUT8_CREATE_NAME: &[u8] = b"DirectInput8Create\0";
|
||||
|
||||
pub(super) const COMPANY_COLLECTION_ADDR: usize = 0x0062be10;
|
||||
pub(super) const CARGO_COLLECTION_ADDR: usize = 0x0062ba8c;
|
||||
pub(super) const SHELL_CONTROLLER_PTR_ADDR: usize = 0x006d4024;
|
||||
pub(super) const SHELL_STATE_PTR_ADDR: usize = 0x006cec74;
|
||||
pub(super) const ACTIVE_MODE_PTR_ADDR: usize = 0x006cec78;
|
||||
pub(super) const SHELL_STATE_SERVICE_ADDR: usize = 0x00482160;
|
||||
pub(super) const SHELL_TRANSITION_MODE_ADDR: usize = 0x00482ec0;
|
||||
pub(super) const PROFILE_STARTUP_DISPATCH_ADDR: usize = 0x00438890;
|
||||
pub(super) const RUNTIME_RESET_ADDR: usize = 0x004336d0;
|
||||
pub(super) const STARTUP_RUNTIME_ALLOC_THUNK_ADDR: usize = 0x0053b070;
|
||||
pub(super) const LOAD_SCREEN_SET_SCALAR_ADDR: usize = 0x004ea710;
|
||||
pub(super) const LOAD_SCREEN_CONSTRUCT_ADDR: usize = 0x004ea620;
|
||||
pub(super) const LOAD_SCREEN_HANDLE_MESSAGE_ADDR: usize = 0x004e3a80;
|
||||
pub(super) const SHELL_RUNTIME_PRIME_ADDR: usize = 0x00538b60;
|
||||
pub(super) const SHELL_FRAME_CYCLE_ADDR: usize = 0x00520620;
|
||||
pub(super) const SHELL_OBJECT_SERVICE_ADDR: usize = 0x0053fda0;
|
||||
pub(super) const SHELL_CHILD_SERVICE_ADDR: usize = 0x005595d0;
|
||||
pub(super) const SHELL_PUBLISH_WINDOW_ADDR: usize = 0x00538e50;
|
||||
pub(super) const SHELL_UNPUBLISH_WINDOW_ADDR: usize = 0x005389c0;
|
||||
pub(super) const SHELL_OBJECT_TEARDOWN_ADDR: usize = 0x005400c0;
|
||||
pub(super) const SHELL_OBJECT_RANGE_REMOVE_ADDR: usize = 0x0053fe00;
|
||||
pub(super) const SHELL_REMOVE_NODE_ADDR: usize = 0x0053f860;
|
||||
pub(super) const SHELL_NODE_VCALL_ADDR: usize = 0x00540910;
|
||||
pub(super) const MODE2_TEARDOWN_ADDR: usize = 0x00502720;
|
||||
pub(super) const SHELL_STATE_ACTIVE_MODE_OFFSET: usize = 0x08;
|
||||
pub(super) const SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET: usize = 0x0c;
|
||||
pub(super) const RUNTIME_PROFILE_PTR_ADDR: usize = 0x006cec7c;
|
||||
pub(super) const RUNTIME_PROFILE_STARTUP_SELECTOR_OFFSET: usize = 0x01;
|
||||
pub(super) const RUNTIME_PROFILE_MANUAL_LOAD_PATH_OFFSET: usize = 0x11;
|
||||
pub(super) const RUNTIME_PROFILE_PENDING_LOAD_BYTE_OFFSET: usize = 0x97;
|
||||
pub(super) const SHELL_MODE_STARTUP_LOAD_DISPATCH: u32 = 1;
|
||||
pub(super) const STARTUP_SELECTOR_SCENARIO_LOAD: u8 = 3;
|
||||
pub(super) const STARTUP_RUNTIME_OBJECT_SIZE: u32 = 0x00046c40;
|
||||
pub(super) const INDEXED_COLLECTION_FLAT_FLAG_OFFSET: usize = 0x04;
|
||||
pub(super) const INDEXED_COLLECTION_STRIDE_OFFSET: usize = 0x08;
|
||||
pub(super) const INDEXED_COLLECTION_ID_BOUND_OFFSET: usize = 0x14;
|
||||
pub(super) const INDEXED_COLLECTION_PAYLOAD_OFFSET: usize = 0x30;
|
||||
pub(super) const INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET: usize = 0x34;
|
||||
pub(super) const CARGO_STEM_OFFSET: usize = 0x04;
|
||||
pub(super) const CARGO_SUBTYPE_OFFSET: usize = 0x32;
|
||||
pub(super) const CARGO_ROUTE_STYLE_OFFSET: usize = 0x46;
|
||||
pub(super) const CARGO_COLLECTION_CLASS_MARKER_BASE_OFFSET: usize = 0x9a;
|
||||
pub(super) const COMPANY_ACTIVE_OFFSET: usize = 0x3f;
|
||||
pub(super) const COMPANY_OUTSTANDING_SHARES_OFFSET: usize = 0x47;
|
||||
pub(super) const COMPANY_COMPANY_VALUE_OFFSET: usize = 0x57;
|
||||
pub(super) const COMPANY_BOND_COUNT_OFFSET: usize = 0x5b;
|
||||
pub(super) const COMPANY_BOND_TABLE_OFFSET: usize = 0x5f;
|
||||
pub(super) const COMPANY_FOUNDING_YEAR_OFFSET: usize = 0x157;
|
||||
pub(super) const COMPANY_LAST_BANKRUPTCY_YEAR_OFFSET: usize = 0x163;
|
||||
pub(super) const COMPANY_CITY_CONNECTION_LATCH_OFFSET: usize = 0x0d18;
|
||||
pub(super) const COMPANY_LINKED_TRANSIT_LATCH_OFFSET: usize = 0x0d56;
|
||||
|
||||
pub(super) const SCENARIO_CURRENT_YEAR_OFFSET: usize = 0x0d;
|
||||
pub(super) const SCENARIO_BUILDING_DENSITY_GROWTH_OFFSET: usize = 0x4c7c;
|
||||
pub(super) const SCENARIO_BANKRUPTCY_TOGGLE_OFFSET: usize = 0x4a8f;
|
||||
pub(super) const SCENARIO_BOND_TOGGLE_OFFSET: usize = 0x4a8b;
|
||||
pub(super) const SCENARIO_STOCK_TOGGLE_OFFSET: usize = 0x4a87;
|
||||
pub(super) const SCENARIO_DIVIDEND_TOGGLE_OFFSET: usize = 0x4a93;
|
||||
|
||||
pub(super) const MAX_CAPTURE_POLL_ATTEMPTS: usize = 120;
|
||||
pub(super) const CAPTURE_POLL_INTERVAL: Duration = Duration::from_secs(1);
|
||||
pub(super) const AUTO_LOAD_READY_POLLS: u32 = 1;
|
||||
pub(super) const AUTO_LOAD_DEFER_POLLS: u32 = 0;
|
||||
pub(super) const MEM_COMMIT: u32 = 0x1000;
|
||||
pub(super) const MEM_RESERVE: u32 = 0x2000;
|
||||
pub(super) const PAGE_EXECUTE_READWRITE: u32 = 0x40;
|
||||
292
crates/rrt-hook/src/windows/detours.rs
Normal file
292
crates/rrt-hook/src/windows/detours.rs
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_state_service_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
) -> i32 {
|
||||
log_shell_state_service_entry(this);
|
||||
let trampoline: ShellStateServiceFn = unsafe { mem::transmute(SHELL_STATE_SERVICE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
log_shell_state_service_return(this, result);
|
||||
log_post_transition_service_state();
|
||||
maybe_service_auto_load_on_main_thread();
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn profile_startup_dispatch_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
arg1: u32,
|
||||
arg2: u32,
|
||||
) -> i32 {
|
||||
log_profile_startup_dispatch_entry(this, arg1, arg2);
|
||||
append_log_message(AUTO_LOAD_PROFILE_DISPATCH_ENTRY_MESSAGE);
|
||||
let trampoline: ProfileStartupDispatchFn =
|
||||
unsafe { mem::transmute(PROFILE_STARTUP_DISPATCH_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, arg1, arg2) };
|
||||
append_log_message(AUTO_LOAD_PROFILE_DISPATCH_RETURNED_MESSAGE);
|
||||
log_profile_startup_dispatch_return(this, arg1, arg2, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn runtime_reset_detour(this: *mut u8, _edx: usize) -> *mut u8 {
|
||||
append_log_message(AUTO_LOAD_RUNTIME_RESET_ENTRY_MESSAGE);
|
||||
log_runtime_reset_entry(this);
|
||||
let trampoline: RuntimeResetFn = unsafe { mem::transmute(RUNTIME_RESET_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
append_log_message(AUTO_LOAD_RUNTIME_RESET_RETURNED_MESSAGE);
|
||||
log_runtime_reset_return(this, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "cdecl" fn allocator_detour(size: u32) -> *mut u8 {
|
||||
let trace = should_trace_allocator(size);
|
||||
let count = if trace {
|
||||
AUTO_LOAD_ALLOCATOR_WINDOW_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_ALLOCATOR_ENTRY_MESSAGE);
|
||||
log_allocator_entry(size, count);
|
||||
}
|
||||
let original: StartupRuntimeAllocThunkFn = unsafe { mem::transmute(0x005a125dusize) };
|
||||
let result = unsafe { original(size) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_ALLOCATOR_RETURNED_MESSAGE);
|
||||
log_allocator_return(size, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn load_screen_scalar_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
value_bits: u32,
|
||||
) -> u32 {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_SCALAR_ENTRY_MESSAGE);
|
||||
log_load_screen_scalar_entry(this, value_bits);
|
||||
let trampoline: LoadScreenSetScalarFn =
|
||||
unsafe { mem::transmute(LOAD_SCREEN_SCALAR_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, value_bits) };
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_SCALAR_RETURNED_MESSAGE);
|
||||
log_load_screen_scalar_return(this, value_bits, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn load_screen_construct_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
) -> *mut u8 {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_ENTRY_MESSAGE);
|
||||
log_load_screen_construct_entry(this);
|
||||
let trampoline: LoadScreenConstructFn =
|
||||
unsafe { mem::transmute(LOAD_SCREEN_CONSTRUCT_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_RETURNED_MESSAGE);
|
||||
log_load_screen_construct_return(this, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn load_screen_message_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
message: *mut u8,
|
||||
) -> i32 {
|
||||
let trace = should_trace_load_screen_message(this);
|
||||
let count = if trace {
|
||||
AUTO_LOAD_LOAD_SCREEN_MESSAGE_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_MESSAGE_ENTRY_MESSAGE);
|
||||
log_load_screen_message_entry(this, message, count);
|
||||
}
|
||||
let trampoline: LoadScreenHandleMessageFn =
|
||||
unsafe { mem::transmute(LOAD_SCREEN_MESSAGE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, message) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_LOAD_SCREEN_MESSAGE_RETURNED_MESSAGE);
|
||||
log_load_screen_message_return(this, message, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn runtime_prime_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
let trace = should_trace_runtime_prime();
|
||||
let count = if trace {
|
||||
AUTO_LOAD_RUNTIME_PRIME_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_RUNTIME_PRIME_ENTRY_MESSAGE);
|
||||
log_runtime_prime_entry(this, count);
|
||||
}
|
||||
let trampoline: ShellRuntimePrimeFn = unsafe { mem::transmute(RUNTIME_PRIME_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_RUNTIME_PRIME_RETURNED_MESSAGE);
|
||||
log_runtime_prime_return(this, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn frame_cycle_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
let trace = should_trace_frame_cycle();
|
||||
let count = if trace {
|
||||
AUTO_LOAD_FRAME_CYCLE_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_FRAME_CYCLE_ENTRY_MESSAGE);
|
||||
log_frame_cycle_entry(this, count);
|
||||
}
|
||||
let trampoline: ShellFrameCycleFn = unsafe { mem::transmute(FRAME_CYCLE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_FRAME_CYCLE_RETURNED_MESSAGE);
|
||||
log_frame_cycle_return(this, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn object_service_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
let trace = should_trace_object_service();
|
||||
let count = if trace {
|
||||
AUTO_LOAD_OBJECT_SERVICE_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_OBJECT_SERVICE_ENTRY_MESSAGE);
|
||||
log_object_service_entry(this, count);
|
||||
}
|
||||
let trampoline: ShellObjectServiceFn = unsafe { mem::transmute(OBJECT_SERVICE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_OBJECT_SERVICE_RETURNED_MESSAGE);
|
||||
log_object_service_return(this, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn child_service_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
let trace = should_trace_child_service();
|
||||
let count = if trace {
|
||||
AUTO_LOAD_CHILD_SERVICE_LOG_COUNT.fetch_add(1, Ordering::AcqRel) + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_CHILD_SERVICE_ENTRY_MESSAGE);
|
||||
log_child_service_entry(this, count);
|
||||
}
|
||||
let trampoline: ShellChildServiceFn = unsafe { mem::transmute(CHILD_SERVICE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
if trace {
|
||||
append_log_message(AUTO_LOAD_CHILD_SERVICE_RETURNED_MESSAGE);
|
||||
log_child_service_return(this, count, result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_publish_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
object: *mut u8,
|
||||
flag: u32,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_PUBLISH_ENTRY_MESSAGE);
|
||||
log_shell_publish_entry(this, object, flag);
|
||||
let trampoline: ShellPublishWindowFn = unsafe { mem::transmute(SHELL_PUBLISH_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, object, flag) };
|
||||
append_log_message(AUTO_LOAD_PUBLISH_RETURNED_MESSAGE);
|
||||
log_shell_publish_return(this, object, flag, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_unpublish_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
object: *mut u8,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_UNPUBLISH_ENTRY_MESSAGE);
|
||||
log_shell_unpublish_entry(this, object);
|
||||
let trampoline: ShellUnpublishWindowFn = unsafe { mem::transmute(SHELL_UNPUBLISH_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, object) };
|
||||
append_log_message(AUTO_LOAD_UNPUBLISH_RETURNED_MESSAGE);
|
||||
log_shell_unpublish_return(this, object, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_object_range_remove_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
arg1: u32,
|
||||
arg2: u32,
|
||||
arg3: u32,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_OBJECT_RANGE_REMOVE_ENTRY_MESSAGE);
|
||||
log_shell_object_range_remove_entry(this, arg1, arg2, arg3);
|
||||
let trampoline: ShellObjectRangeRemoveFn =
|
||||
unsafe { mem::transmute(SHELL_OBJECT_RANGE_REMOVE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, arg1, arg2, arg3) };
|
||||
append_log_message(AUTO_LOAD_OBJECT_RANGE_REMOVE_RETURNED_MESSAGE);
|
||||
log_shell_object_range_remove_return(this, arg1, arg2, arg3, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_object_teardown_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_OBJECT_TEARDOWN_ENTRY_MESSAGE);
|
||||
log_shell_object_teardown_entry(this);
|
||||
let trampoline: ShellObjectTeardownFn =
|
||||
unsafe { mem::transmute(SHELL_OBJECT_TEARDOWN_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
append_log_message(AUTO_LOAD_OBJECT_TEARDOWN_RETURNED_MESSAGE);
|
||||
log_shell_object_teardown_return(this, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_node_vcall_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
record: *mut u8,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_NODE_VCALL_ENTRY_MESSAGE);
|
||||
log_shell_node_vcall_entry(this, record);
|
||||
let trampoline: ShellNodeVcallFn = unsafe { mem::transmute(SHELL_NODE_VCALL_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, record) };
|
||||
append_log_message(AUTO_LOAD_NODE_VCALL_RETURNED_MESSAGE);
|
||||
log_shell_node_vcall_return(this, record, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn shell_remove_node_detour(
|
||||
this: *mut u8,
|
||||
_edx: usize,
|
||||
node: *mut u8,
|
||||
) -> i32 {
|
||||
append_log_message(AUTO_LOAD_REMOVE_NODE_ENTRY_MESSAGE);
|
||||
log_shell_remove_node_entry(this, node);
|
||||
let trampoline: ShellRemoveNodeFn = unsafe { mem::transmute(SHELL_REMOVE_NODE_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this, node) };
|
||||
append_log_message(AUTO_LOAD_REMOVE_NODE_RETURNED_MESSAGE);
|
||||
log_shell_remove_node_return(this, node, result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "fastcall" fn mode2_teardown_detour(this: *mut u8, _edx: usize) -> i32 {
|
||||
append_log_message(AUTO_LOAD_MODE2_TEARDOWN_ENTRY_MESSAGE);
|
||||
log_mode2_teardown_entry(this);
|
||||
let trampoline: Mode2TeardownFn = unsafe { mem::transmute(MODE2_TEARDOWN_TRAMPOLINE) };
|
||||
let result = unsafe { trampoline(this) };
|
||||
append_log_message(AUTO_LOAD_MODE2_TEARDOWN_RETURNED_MESSAGE);
|
||||
log_mode2_teardown_return(this, result);
|
||||
result
|
||||
}
|
||||
86
crates/rrt-hook/src/windows/ffi.rs
Normal file
86
crates/rrt-hook/src/windows/ffi.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use super::*;
|
||||
|
||||
unsafe extern "system" {
|
||||
pub(super) fn CreateFileA(
|
||||
lp_file_name: *const c_char,
|
||||
desired_access: u32,
|
||||
share_mode: u32,
|
||||
security_attributes: *mut c_void,
|
||||
creation_disposition: u32,
|
||||
flags_and_attributes: u32,
|
||||
template_file: *mut c_void,
|
||||
) -> isize;
|
||||
pub(super) fn SetFilePointer(
|
||||
file: isize,
|
||||
distance: i32,
|
||||
distance_high: *mut i32,
|
||||
move_method: u32,
|
||||
) -> u32;
|
||||
pub(super) fn WriteFile(
|
||||
file: isize,
|
||||
buffer: *const c_void,
|
||||
bytes_to_write: u32,
|
||||
bytes_written: *mut u32,
|
||||
overlapped: *mut c_void,
|
||||
) -> i32;
|
||||
pub(super) fn CloseHandle(handle: isize) -> i32;
|
||||
pub(super) fn DisableThreadLibraryCalls(module: *mut c_void) -> i32;
|
||||
pub(super) fn FlushInstructionCache(
|
||||
process: *mut c_void,
|
||||
base_address: *const c_void,
|
||||
size: usize,
|
||||
) -> i32;
|
||||
pub(super) fn GetCurrentProcess() -> *mut c_void;
|
||||
pub(super) fn GetSystemDirectoryA(buffer: *mut u8, size: u32) -> u32;
|
||||
pub(super) fn GetProcAddress(module: isize, name: *const c_char) -> *mut c_void;
|
||||
pub(super) fn LoadLibraryA(name: *const c_char) -> isize;
|
||||
pub(super) fn OutputDebugStringA(output: *const c_char);
|
||||
pub(super) fn VirtualAlloc(
|
||||
address: *mut c_void,
|
||||
size: usize,
|
||||
allocation_type: u32,
|
||||
protect: u32,
|
||||
) -> *mut c_void;
|
||||
pub(super) fn VirtualProtect(
|
||||
address: *mut c_void,
|
||||
size: usize,
|
||||
new_protect: u32,
|
||||
old_protect: *mut u32,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Guid {
|
||||
data1: u32,
|
||||
data2: u16,
|
||||
data3: u16,
|
||||
data4: [u8; 8],
|
||||
}
|
||||
|
||||
pub(super) type DirectInput8CreateFn = unsafe extern "system" fn(
|
||||
instance: *mut c_void,
|
||||
version: u32,
|
||||
riid: *const Guid,
|
||||
out: *mut *mut c_void,
|
||||
outer: *mut c_void,
|
||||
) -> i32;
|
||||
pub(super) type ShellStateServiceFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellTransitionModeFn = unsafe extern "thiscall" fn(*mut u8, u32, u32) -> i32;
|
||||
pub(super) type ProfileStartupDispatchFn = unsafe extern "thiscall" fn(*mut u8, u32, u32) -> i32;
|
||||
pub(super) type RuntimeResetFn = unsafe extern "thiscall" fn(*mut u8) -> *mut u8;
|
||||
pub(super) type StartupRuntimeAllocThunkFn = unsafe extern "cdecl" fn(u32) -> *mut u8;
|
||||
pub(super) type LoadScreenSetScalarFn = unsafe extern "thiscall" fn(*mut u8, u32) -> u32;
|
||||
pub(super) type LoadScreenConstructFn = unsafe extern "thiscall" fn(*mut u8) -> *mut u8;
|
||||
pub(super) type LoadScreenHandleMessageFn = unsafe extern "thiscall" fn(*mut u8, *mut u8) -> i32;
|
||||
pub(super) type ShellRuntimePrimeFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellFrameCycleFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellObjectServiceFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellChildServiceFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellPublishWindowFn = unsafe extern "thiscall" fn(*mut u8, *mut u8, u32) -> i32;
|
||||
pub(super) type ShellUnpublishWindowFn = unsafe extern "thiscall" fn(*mut u8, *mut u8) -> i32;
|
||||
pub(super) type ShellObjectTeardownFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
pub(super) type ShellObjectRangeRemoveFn =
|
||||
unsafe extern "thiscall" fn(*mut u8, u32, u32, u32) -> i32;
|
||||
pub(super) type ShellRemoveNodeFn = unsafe extern "thiscall" fn(*mut u8, *mut u8) -> i32;
|
||||
pub(super) type ShellNodeVcallFn = unsafe extern "thiscall" fn(*mut u8, *mut u8) -> i32;
|
||||
pub(super) type Mode2TeardownFn = unsafe extern "thiscall" fn(*mut u8) -> i32;
|
||||
383
crates/rrt-hook/src/windows/install.rs
Normal file
383
crates/rrt-hook/src/windows/install.rs
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) unsafe fn install_shell_state_service_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = SHELL_STATE_SERVICE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_state_service_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_STATE_SERVICE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_profile_startup_dispatch_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 16;
|
||||
let target = PROFILE_STARTUP_DISPATCH_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
profile_startup_dispatch_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { PROFILE_STARTUP_DISPATCH_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_runtime_reset_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 16;
|
||||
let target = RUNTIME_RESET_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
runtime_reset_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { RUNTIME_RESET_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_allocator_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 5;
|
||||
let target = STARTUP_RUNTIME_ALLOC_THUNK_ADDR as *mut u8;
|
||||
let trampoline =
|
||||
unsafe { install_rel32_detour(target, STOLEN_LEN, allocator_detour as *const () as usize) };
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { ALLOCATOR_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_load_screen_scalar_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 10;
|
||||
let target = LOAD_SCREEN_SET_SCALAR_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
load_screen_scalar_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { LOAD_SCREEN_SCALAR_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_load_screen_construct_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = LOAD_SCREEN_CONSTRUCT_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
load_screen_construct_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { LOAD_SCREEN_CONSTRUCT_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_load_screen_message_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 25;
|
||||
let target = LOAD_SCREEN_HANDLE_MESSAGE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
load_screen_message_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { LOAD_SCREEN_MESSAGE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_runtime_prime_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 12;
|
||||
let target = SHELL_RUNTIME_PRIME_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
runtime_prime_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { RUNTIME_PRIME_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_frame_cycle_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = SHELL_FRAME_CYCLE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(target, STOLEN_LEN, frame_cycle_detour as *const () as usize)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { FRAME_CYCLE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_object_service_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = SHELL_OBJECT_SERVICE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
object_service_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { OBJECT_SERVICE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_child_service_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = SHELL_CHILD_SERVICE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
child_service_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { CHILD_SERVICE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_publish_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 6;
|
||||
let target = SHELL_PUBLISH_WINDOW_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_publish_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_PUBLISH_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_unpublish_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 10;
|
||||
let target = SHELL_UNPUBLISH_WINDOW_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_unpublish_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_UNPUBLISH_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_object_teardown_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 7;
|
||||
let target = SHELL_OBJECT_TEARDOWN_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_object_teardown_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_OBJECT_TEARDOWN_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_object_range_remove_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 12;
|
||||
let target = SHELL_OBJECT_RANGE_REMOVE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_object_range_remove_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_OBJECT_RANGE_REMOVE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_node_vcall_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 21;
|
||||
let target = SHELL_NODE_VCALL_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_node_vcall_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_NODE_VCALL_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_shell_remove_node_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 10;
|
||||
let target = SHELL_REMOVE_NODE_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
shell_remove_node_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { SHELL_REMOVE_NODE_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_mode2_teardown_hook() -> bool {
|
||||
const STOLEN_LEN: usize = 14;
|
||||
let target = MODE2_TEARDOWN_ADDR as *mut u8;
|
||||
let trampoline = unsafe {
|
||||
install_rel32_detour(
|
||||
target,
|
||||
STOLEN_LEN,
|
||||
mode2_teardown_detour as *const () as usize,
|
||||
)
|
||||
};
|
||||
if trampoline == 0 {
|
||||
return false;
|
||||
}
|
||||
unsafe { MODE2_TEARDOWN_TRAMPOLINE = trampoline };
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) unsafe fn install_rel32_detour(
|
||||
target: *mut u8,
|
||||
stolen_len: usize,
|
||||
detour: usize,
|
||||
) -> usize {
|
||||
let trampoline_size = stolen_len + 5;
|
||||
let trampoline = unsafe {
|
||||
VirtualAlloc(
|
||||
ptr::null_mut(),
|
||||
trampoline_size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
)
|
||||
} as *mut u8;
|
||||
if trampoline.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsafe { ptr::copy_nonoverlapping(target, trampoline, stolen_len) };
|
||||
unsafe { write_rel32_jump(trampoline.add(stolen_len), target.add(stolen_len) as usize) };
|
||||
|
||||
let mut old_protect = 0_u32;
|
||||
if unsafe {
|
||||
VirtualProtect(
|
||||
target.cast(),
|
||||
stolen_len,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&mut old_protect,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsafe { write_rel32_jump(target, detour) };
|
||||
for offset in 5..stolen_len {
|
||||
unsafe { ptr::write(target.add(offset), 0x90) };
|
||||
}
|
||||
let mut restore_protect = 0_u32;
|
||||
let _ = unsafe { VirtualProtect(target.cast(), stolen_len, old_protect, &mut restore_protect) };
|
||||
let _ = unsafe { FlushInstructionCache(GetCurrentProcess(), target.cast(), stolen_len) };
|
||||
trampoline as usize
|
||||
}
|
||||
|
||||
pub(super) unsafe fn write_rel32_jump(location: *mut u8, destination: usize) {
|
||||
unsafe { ptr::write(location, 0xE9) };
|
||||
let next_ip = unsafe { location.add(5) } as usize;
|
||||
let relative = (destination as isize - next_ip as isize) as i32;
|
||||
unsafe { ptr::write_unaligned(location.add(1).cast::<i32>(), relative) };
|
||||
}
|
||||
|
||||
pub(super) unsafe fn load_direct_input8_create() -> Option<DirectInput8CreateFn> {
|
||||
if let Some(callback) = unsafe { REAL_DINPUT8_CREATE } {
|
||||
return Some(callback);
|
||||
}
|
||||
|
||||
let mut system_directory = [0_u8; 260];
|
||||
let length = unsafe {
|
||||
GetSystemDirectoryA(system_directory.as_mut_ptr(), system_directory.len() as u32)
|
||||
};
|
||||
if length == 0 || length as usize >= system_directory.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut dll_path = system_directory[..length as usize].to_vec();
|
||||
dll_path.extend_from_slice(br"\dinput8.dll");
|
||||
dll_path.push(0);
|
||||
|
||||
let module = unsafe { LoadLibraryA(dll_path.as_ptr().cast()) };
|
||||
if module == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let symbol = unsafe { GetProcAddress(module, DIRECT_INPUT8_CREATE_NAME.as_ptr().cast()) };
|
||||
if symbol.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let callback: DirectInput8CreateFn = unsafe { mem::transmute(symbol) };
|
||||
unsafe {
|
||||
REAL_DINPUT8_CREATE = Some(callback);
|
||||
}
|
||||
Some(callback)
|
||||
}
|
||||
1096
crates/rrt-hook/src/windows/logging.rs
Normal file
1096
crates/rrt-hook/src/windows/logging.rs
Normal file
File diff suppressed because it is too large
Load diff
99
crates/rrt-hook/src/windows/memory.rs
Normal file
99
crates/rrt-hook/src/windows/memory.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) unsafe fn resolve_active_mode_ptr() -> *mut u8 {
|
||||
let global_active_mode = unsafe { resolve_global_active_mode_ptr() };
|
||||
if !global_active_mode.is_null() {
|
||||
return global_active_mode;
|
||||
}
|
||||
|
||||
let shell_state = unsafe { read_ptr(SHELL_STATE_PTR_ADDR as *const u8) };
|
||||
if shell_state.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
unsafe { read_ptr(shell_state.add(SHELL_STATE_ACTIVE_MODE_OBJECT_OFFSET)) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn resolve_global_active_mode_ptr() -> *mut u8 {
|
||||
unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn indexed_collection_entry_id_is_live(
|
||||
collection: *const u8,
|
||||
entry_id: usize,
|
||||
) -> bool {
|
||||
let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) };
|
||||
if entry_id == 0 || entry_id > id_bound.max(0) as usize {
|
||||
return false;
|
||||
}
|
||||
|
||||
let tombstone_bits =
|
||||
unsafe { read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) };
|
||||
if tombstone_bits.is_null() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let bit_index = entry_id as u32;
|
||||
let word =
|
||||
unsafe { ptr::read_unaligned(tombstone_bits.add((bit_index / 32) as usize).cast::<u32>()) };
|
||||
(word & (1_u32 << (bit_index % 32))) == 0
|
||||
}
|
||||
|
||||
pub(super) unsafe fn indexed_collection_resolve_live_entry_by_id(
|
||||
collection: *const u8,
|
||||
entry_id: usize,
|
||||
) -> *mut u8 {
|
||||
if !unsafe { indexed_collection_entry_id_is_live(collection, entry_id) } {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let payload = unsafe { read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) };
|
||||
if payload.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let stride = unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) as usize };
|
||||
let flat = unsafe { read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 };
|
||||
|
||||
if flat {
|
||||
unsafe { payload.add(stride * entry_id) }
|
||||
} else {
|
||||
unsafe { ptr::read_unaligned(payload.add(stride * entry_id).cast::<*mut u8>()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_u8(address: *const u8) -> u8 {
|
||||
unsafe { ptr::read_unaligned(address) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_u16(address: *const u8) -> u16 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<u16>()) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_u32(address: *const u8) -> u32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<u32>()) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_i32(address: *const u8) -> i32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<i32>()) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_f32(address: *const u8) -> f32 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<f32>()) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_ptr(address: *const u8) -> *mut u8 {
|
||||
unsafe { ptr::read_unaligned(address.cast::<*mut u8>()) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn read_c_string(address: *const u8, max_len: usize) -> String {
|
||||
let mut len = 0;
|
||||
while len < max_len {
|
||||
let byte = unsafe { read_u8(address.add(len)) };
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
String::from_utf8_lossy(unsafe { std::slice::from_raw_parts(address, len) }).into_owned()
|
||||
}
|
||||
71
crates/rrt-hook/src/windows/mod.rs
Normal file
71
crates/rrt-hook/src/windows/mod.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
use crate::capture::{
|
||||
CargoCollectionProbe, CargoCollectionProbeRow, IndexedCollectionProbe,
|
||||
IndexedCollectionProbeRow, sample_finance_snapshot, write_cargo_collection_probe,
|
||||
write_finance_snapshot_bundle, write_finance_snapshot_only, write_indexed_collection_probe,
|
||||
};
|
||||
use core::ffi::{c_char, c_void};
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use std::env;
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use rrt_model::finance::{
|
||||
AnnualFinancePolicy, BondPosition, CompanyFinanceState, FinanceSnapshot, GrowthSetting,
|
||||
};
|
||||
|
||||
mod auto_load;
|
||||
mod capture;
|
||||
mod constants;
|
||||
mod detours;
|
||||
mod ffi;
|
||||
mod install;
|
||||
mod logging;
|
||||
mod memory;
|
||||
mod state;
|
||||
|
||||
use auto_load::*;
|
||||
use capture::*;
|
||||
use constants::*;
|
||||
use detours::*;
|
||||
use ffi::*;
|
||||
use install::*;
|
||||
use logging::*;
|
||||
use memory::*;
|
||||
use state::*;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn DllMain(module: *mut c_void, reason: u32, _reserved: *mut c_void) -> i32 {
|
||||
if reason == DLL_PROCESS_ATTACH {
|
||||
unsafe {
|
||||
let _ = DisableThreadLibraryCalls(module);
|
||||
OutputDebugStringA(DEBUG_MESSAGE.as_ptr().cast());
|
||||
append_attach_log();
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "system" fn DirectInput8Create(
|
||||
instance: *mut c_void,
|
||||
version: u32,
|
||||
riid: *const Guid,
|
||||
out: *mut *mut c_void,
|
||||
outer: *mut c_void,
|
||||
) -> i32 {
|
||||
maybe_emit_finance_template_bundle();
|
||||
maybe_start_finance_capture_thread();
|
||||
maybe_start_cargo_capture_thread();
|
||||
maybe_install_auto_load_hook();
|
||||
|
||||
let direct_input8_create = unsafe { load_direct_input8_create() };
|
||||
match direct_input8_create {
|
||||
Some(callback) => unsafe { callback(instance, version, riid, out, outer) },
|
||||
None => E_FAIL,
|
||||
}
|
||||
}
|
||||
45
crates/rrt-hook/src/windows/state.rs
Normal file
45
crates/rrt-hook/src/windows/state.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use super::*;
|
||||
|
||||
pub(super) static mut REAL_DINPUT8_CREATE: Option<DirectInput8CreateFn> = None;
|
||||
pub(super) static FINANCE_TEMPLATE_EMITTED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static FINANCE_CAPTURE_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static FINANCE_COLLECTION_PROBE_WRITTEN: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static CARGO_CAPTURE_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_THREAD_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_HOOK_INSTALLED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_ATTEMPTED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_DEFERRED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_TRANSITION_ARMED: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_LAST_GATE_MASK: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
pub(super) static AUTO_LOAD_READY_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_ARMED_TICK_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_ALLOCATOR_WINDOW_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||
pub(super) static AUTO_LOAD_ALLOCATOR_WINDOW_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_POST_TRANSITION_SERVICE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_LOAD_SCREEN_MESSAGE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_SERVICE_ENTRY_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_SERVICE_RETURN_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_RUNTIME_PRIME_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_FRAME_CYCLE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_OBJECT_SERVICE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_CHILD_SERVICE_LOG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub(super) static AUTO_LOAD_SAVE_STEM: OnceLock<String> = OnceLock::new();
|
||||
pub(super) static mut SHELL_STATE_SERVICE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut PROFILE_STARTUP_DISPATCH_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut RUNTIME_RESET_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut ALLOCATOR_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut LOAD_SCREEN_SCALAR_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut LOAD_SCREEN_CONSTRUCT_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut LOAD_SCREEN_MESSAGE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut RUNTIME_PRIME_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut FRAME_CYCLE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut OBJECT_SERVICE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut CHILD_SERVICE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_PUBLISH_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_UNPUBLISH_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_OBJECT_TEARDOWN_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_OBJECT_RANGE_REMOVE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_REMOVE_NODE_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut SHELL_NODE_VCALL_TRAMPOLINE: usize = 0;
|
||||
pub(super) static mut MODE2_TEARDOWN_TRAMPOLINE: usize = 0;
|
||||
Loading…
Add table
Add a link
Reference in a new issue