Instrument FIFA17 SBC completion dispatch
This commit is contained in:
@@ -93,7 +93,7 @@ static NOTIFIER_COUNT: AtomicUsize = AtomicUsize::new(usize::MAX);
|
||||
static PATCH_INSTALLER_BUSY: AtomicBool = AtomicBool::new(false);
|
||||
static CODE_PATCH_PENDING: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct PatchInstallerGate;
|
||||
pub(crate) struct PatchInstallerGate;
|
||||
|
||||
impl Drop for PatchInstallerGate {
|
||||
fn drop(&mut self) {
|
||||
@@ -101,7 +101,7 @@ impl Drop for PatchInstallerGate {
|
||||
}
|
||||
}
|
||||
|
||||
fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
|
||||
pub(crate) fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
|
||||
for _ in 0..200 {
|
||||
if PATCH_INSTALLER_BUSY
|
||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||
@@ -114,7 +114,7 @@ fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
|
||||
None
|
||||
}
|
||||
|
||||
struct CodeInstallerPending;
|
||||
pub(crate) struct CodeInstallerPending;
|
||||
|
||||
impl Drop for CodeInstallerPending {
|
||||
fn drop(&mut self) {
|
||||
@@ -138,15 +138,15 @@ enum TraceState {
|
||||
DegradedHookAndProcess,
|
||||
}
|
||||
|
||||
fn env_enabled(value: Option<&str>) -> bool {
|
||||
pub(crate) fn env_enabled(value: Option<&str>) -> bool {
|
||||
matches!(value, Some("1"))
|
||||
}
|
||||
|
||||
fn target_va(base: usize, rva: usize) -> Option<usize> {
|
||||
pub(crate) fn target_va(base: usize, rva: usize) -> Option<usize> {
|
||||
base.checked_add(rva)
|
||||
}
|
||||
|
||||
fn absolute_jump(destination: usize) -> [u8; ABS_JUMP_LEN] {
|
||||
pub(crate) fn absolute_jump(destination: usize) -> [u8; ABS_JUMP_LEN] {
|
||||
let mut jump = [0u8; ABS_JUMP_LEN];
|
||||
jump[..6].copy_from_slice(&[0xff, 0x25, 0, 0, 0, 0]);
|
||||
jump[6..].copy_from_slice(&(destination as u64).to_le_bytes());
|
||||
@@ -160,7 +160,7 @@ fn instruction_pointer_in_span(rip: usize, target: usize) -> bool {
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
struct SuspendedPeers {
|
||||
pub(crate) struct SuspendedPeers {
|
||||
handles: [HANDLE; MAX_PEERS],
|
||||
tids: [u32; MAX_PEERS],
|
||||
count: usize,
|
||||
@@ -179,7 +179,7 @@ impl SuspendedPeers {
|
||||
self.tids[..self.count].contains(&tid)
|
||||
}
|
||||
|
||||
unsafe fn resume_all(&mut self) -> bool {
|
||||
pub(crate) unsafe fn resume_all(&mut self) -> bool {
|
||||
let mut all_resumed = true;
|
||||
for index in (0..self.count).rev() {
|
||||
let handle = self.handles[index];
|
||||
@@ -208,14 +208,14 @@ impl Drop for SuspendedPeers {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum QuiesceFailure {
|
||||
pub(crate) enum QuiesceFailure {
|
||||
Acquire,
|
||||
Resume,
|
||||
}
|
||||
|
||||
/// Stop and inspect every peer thread before touching either entry point. Any
|
||||
/// incomplete enumeration/access/context operation fails the transaction closed.
|
||||
unsafe fn suspend_peers(
|
||||
pub(crate) unsafe fn suspend_peers(
|
||||
factory: usize,
|
||||
deserializer: usize,
|
||||
) -> Result<SuspendedPeers, QuiesceFailure> {
|
||||
@@ -329,7 +329,7 @@ unsafe fn executable_range(address: usize, length: usize) -> bool {
|
||||
) && end <= mbi.BaseAddress as usize + mbi.RegionSize
|
||||
}
|
||||
|
||||
unsafe fn executable_range_in_image(base: usize, address: usize, length: usize) -> bool {
|
||||
pub(crate) unsafe fn executable_range_in_image(base: usize, address: usize, length: usize) -> bool {
|
||||
let Some(end) = address.checked_add(length) else {
|
||||
return false;
|
||||
};
|
||||
@@ -347,7 +347,7 @@ unsafe fn executable_range_in_image(base: usize, address: usize, length: usize)
|
||||
&& end <= mbi.BaseAddress as usize + mbi.RegionSize
|
||||
}
|
||||
|
||||
unsafe fn readable_range(address: usize, length: usize) -> bool {
|
||||
pub(crate) unsafe fn readable_range(address: usize, length: usize) -> bool {
|
||||
let Some(end) = address.checked_add(length) else {
|
||||
return false;
|
||||
};
|
||||
@@ -362,20 +362,20 @@ unsafe fn readable_range(address: usize, length: usize) -> bool {
|
||||
&& end <= mbi.BaseAddress as usize + mbi.RegionSize
|
||||
}
|
||||
|
||||
unsafe fn guarded_usize(address: usize) -> Option<usize> {
|
||||
pub(crate) unsafe fn guarded_usize(address: usize) -> Option<usize> {
|
||||
(address & 7 == 0 && readable_range(address, 8))
|
||||
.then(|| core::ptr::read_volatile(address as *const usize))
|
||||
}
|
||||
|
||||
unsafe fn guarded_u16(address: usize) -> Option<u16> {
|
||||
pub(crate) unsafe fn guarded_u16(address: usize) -> Option<u16> {
|
||||
readable_range(address, 2).then(|| core::ptr::read_volatile(address as *const u16))
|
||||
}
|
||||
|
||||
unsafe fn guarded_u8(address: usize) -> Option<u8> {
|
||||
pub(crate) unsafe fn guarded_u8(address: usize) -> Option<u8> {
|
||||
readable_range(address, 1).then(|| core::ptr::read_volatile(address as *const u8))
|
||||
}
|
||||
|
||||
unsafe fn valid_cards_image(base: usize) -> bool {
|
||||
pub(crate) unsafe fn valid_cards_image(base: usize) -> bool {
|
||||
let Some(control) = base.checked_add(CONTROL_RVA) else {
|
||||
return false;
|
||||
};
|
||||
@@ -413,7 +413,7 @@ unsafe fn signature_matches(target: usize, signature: &[u8; 32]) -> bool {
|
||||
core::slice::from_raw_parts(target as *const u8, signature.len()) == signature
|
||||
}
|
||||
|
||||
unsafe fn allocate_trampoline(target: usize, copy_len: usize) -> Option<usize> {
|
||||
pub(crate) unsafe fn allocate_trampoline(target: usize, copy_len: usize) -> Option<usize> {
|
||||
let trampoline_len = copy_len.checked_add(ABS_JUMP_LEN)?;
|
||||
let memory = VirtualAlloc(
|
||||
core::ptr::null(),
|
||||
@@ -595,7 +595,10 @@ unsafe extern "system" fn controller_register_wrapper(controller: *mut c_void, e
|
||||
core::mem::transmute(CONTROLLER_REGISTER_TRAMPOLINE.load(Ordering::Acquire));
|
||||
original(controller, event);
|
||||
if event == FUT_SBS_CATEGORIES_EVENT {
|
||||
crate::sbc_hook::note_sbc_controller(controller as usize);
|
||||
crate::sbc_dispatch::note_sbc_controller(
|
||||
controller as usize,
|
||||
TRACE_BASE.load(Ordering::Acquire),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +633,6 @@ unsafe extern "system" fn notifier_wrapper(ctx: *mut c_void) {
|
||||
let original: unsafe extern "system" fn(*mut c_void) =
|
||||
core::mem::transmute(NOTIFIER_TRAMPOLINE.load(Ordering::Acquire));
|
||||
original(ctx);
|
||||
crate::sbc_hook::commit_after_native_parse();
|
||||
NOTIFIER_BYTE_AFTER.store(
|
||||
address
|
||||
.checked_add(0x88)
|
||||
@@ -688,6 +690,44 @@ unsafe extern "system" fn deserializer_wrapper(this: *mut c_void, reader: *mut c
|
||||
DESERIALIZER_EXITS.fetch_add(1, Ordering::Release);
|
||||
result
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct DispatchEvidence {
|
||||
pub(crate) base: usize,
|
||||
pub(crate) factory_entries: u64,
|
||||
pub(crate) factory_exits: u64,
|
||||
pub(crate) factory_result: usize,
|
||||
pub(crate) factory_thread: usize,
|
||||
pub(crate) deserializer_entries: u64,
|
||||
pub(crate) deserializer_exits: u64,
|
||||
pub(crate) deserializer_this: usize,
|
||||
pub(crate) deserializer_reader: usize,
|
||||
pub(crate) deserializer_result: bool,
|
||||
pub(crate) deserializer_thread: usize,
|
||||
pub(crate) model: usize,
|
||||
pub(crate) category_count: usize,
|
||||
pub(crate) notifier_entries: u64,
|
||||
pub(crate) notifier_exits: u64,
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_evidence() -> DispatchEvidence {
|
||||
DispatchEvidence {
|
||||
base: TRACE_BASE.load(Ordering::Acquire),
|
||||
factory_entries: FACTORY_ENTRIES.load(Ordering::Acquire),
|
||||
factory_exits: FACTORY_EXITS.load(Ordering::Acquire),
|
||||
factory_result: FACTORY_LAST_RESULT.load(Ordering::Acquire),
|
||||
factory_thread: FACTORY_LAST_THREAD.load(Ordering::Relaxed),
|
||||
deserializer_entries: DESERIALIZER_ENTRIES.load(Ordering::Acquire),
|
||||
deserializer_exits: DESERIALIZER_EXITS.load(Ordering::Acquire),
|
||||
deserializer_this: DESERIALIZER_LAST_THIS.load(Ordering::Relaxed),
|
||||
deserializer_reader: DESERIALIZER_LAST_READER.load(Ordering::Relaxed),
|
||||
deserializer_result: DESERIALIZER_LAST_RESULT.load(Ordering::Acquire),
|
||||
deserializer_thread: DESERIALIZER_LAST_THREAD.load(Ordering::Relaxed),
|
||||
model: DESERIALIZER_EXIT_M.load(Ordering::Relaxed),
|
||||
category_count: DESERIALIZER_EXIT_COUNT.load(Ordering::Relaxed),
|
||||
notifier_entries: NOTIFIER_ENTRIES.load(Ordering::Acquire),
|
||||
notifier_exits: NOTIFIER_EXITS.load(Ordering::Acquire),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum InstallOutcome {
|
||||
@@ -1103,10 +1143,15 @@ fn install_notifier(enabled: bool) {
|
||||
}
|
||||
|
||||
pub(crate) fn install() {
|
||||
let enabled = env_enabled(std::env::var("OPENFUT_SBC_TRACE").ok().as_deref());
|
||||
let notifier_enabled = env_enabled(std::env::var("OPENFUT_SBC_NOTIFIER_TRACE").ok().as_deref());
|
||||
let dispatch_repair = env_enabled(std::env::var("OPENFUT_SBC_DISPATCH").ok().as_deref());
|
||||
let dispatch_trace =
|
||||
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_DISPATCH_TRACE").ok().as_deref());
|
||||
let enabled =
|
||||
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_TRACE").ok().as_deref());
|
||||
let notifier_enabled =
|
||||
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_NOTIFIER_TRACE").ok().as_deref());
|
||||
CODE_PATCH_PENDING.store(
|
||||
enabled as usize + (notifier_enabled as usize * 2),
|
||||
enabled as usize + (notifier_enabled as usize * 2) + dispatch_trace as usize,
|
||||
Ordering::Release,
|
||||
);
|
||||
install_notifier(notifier_enabled);
|
||||
|
||||
Reference in New Issue
Block a user