diff --git a/src/emulator/gba/mednafen.rs b/src/emulator/gba/mednafen.rs index 6b9e4c3..e40cb64 100644 --- a/src/emulator/gba/mednafen.rs +++ b/src/emulator/gba/mednafen.rs @@ -20,7 +20,7 @@ impl State { if self.is_64_bit { self.cached_ewram_pointer = { const SIG: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF FF 03 00"); - let ptr: Address = SIG.scan_process_range(game, main_module_range)? + 3; + let ptr: Address = SIG.scan_once(game, main_module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -36,7 +36,7 @@ impl State { self.cached_iwram_pointer = { const SIG2: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF 7F 00 00"); - let ptr: Address = SIG2.scan_process_range(game, main_module_range)? + 3; + let ptr: Address = SIG2.scan_once(game, main_module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -56,13 +56,13 @@ impl State { } else { self.cached_ewram_pointer = { const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF FF 03 00"); - let ptr = SIG.scan_process_range(game, main_module_range)?; + let ptr = SIG.scan_once(game, main_module_range)?; game.read::(ptr + 1).ok()?.into() }; self.cached_iwram_pointer = { const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00"); - let ptr = SIG2.scan_process_range(game, main_module_range)?; + let ptr = SIG2.scan_once(game, main_module_range)?; game.read::(ptr + 1).ok()?.into() }; diff --git a/src/emulator/gba/nocashgba.rs b/src/emulator/gba/nocashgba.rs index a7d2a4c..27845d9 100644 --- a/src/emulator/gba/nocashgba.rs +++ b/src/emulator/gba/nocashgba.rs @@ -16,7 +16,7 @@ impl State { .find_map(|(name, _)| game.get_module_range(name).ok())?; self.base_addr = game - .read::(SIG.scan_process_range(game, main_module_range)? + 0x2) + .read::(SIG.scan_once(game, main_module_range)? + 0x2) .ok()? .into(); diff --git a/src/emulator/gba/retroarch.rs b/src/emulator/gba/retroarch.rs index c708ce7..282758f 100644 --- a/src/emulator/gba/retroarch.rs +++ b/src/emulator/gba/retroarch.rs @@ -47,7 +47,7 @@ impl State { const SIG2: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF 7F 00 00"); let ewram_pointer = { - let ptr: Address = SIG.scan_process_range(game, module_range)? + 3; + let ptr: Address = SIG.scan_once(game, module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -61,7 +61,7 @@ impl State { }; let iwram_pointer = { - let ptr: Address = SIG2.scan_process_range(game, module_range)? + 3; + let ptr: Address = SIG2.scan_once(game, module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -85,12 +85,12 @@ impl State { } else { let ewram_pointer: Address = { const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF FF 03 00"); - let ptr = SIG.scan_process_range(game, module_range)?; + let ptr = SIG.scan_once(game, module_range)?; game.read::(ptr + 1).ok()?.into() }; let iwram_pointer: Address = { const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00"); - let ptr = SIG2.scan_process_range(game, module_range)?; + let ptr = SIG2.scan_once(game, module_range)?; game.read::(ptr + 1).ok()?.into() }; @@ -114,24 +114,24 @@ impl State { let base_addr: Address = match is_64_bit { true => { const SIG: Signature<10> = Signature::new("48 8B 15 ?? ?? ?? ?? 8B 42 40"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 3; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 3; let ptr: Address = ptr + 0x4 + game.read::(ptr).ok()?; game.read::(ptr).ok()?.into() } false => { const SIG: Signature<11> = Signature::new("A3 ?? ?? ?? ?? F7 C5 02 00 00 00"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 1; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 1; game.read::(ptr).ok()?.into() } }; let ewram = { - let offset = SIG_EWRAM.scan_process_range(game, (self.core_base, module_size))? + 8; + let offset = SIG_EWRAM.scan_once(game, (self.core_base, module_size))? + 8; base_addr + game.read::(offset).ok()? }; let iwram = { - let offset = SIG_IWRAM.scan_process_range(game, (self.core_base, module_size))? + 9; + let offset = SIG_IWRAM.scan_once(game, (self.core_base, module_size))? + 9; base_addr + game.read::(offset).ok()? }; diff --git a/src/emulator/gba/vba.rs b/src/emulator/gba/vba.rs index 62f05e8..82448f2 100644 --- a/src/emulator/gba/vba.rs +++ b/src/emulator/gba/vba.rs @@ -25,7 +25,7 @@ impl State { const SIG2: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E3 FF 7F 00 00"); self.cached_ewram_pointer = { - let ptr: Address = SIG.scan_process_range(game, main_module_range)? + 3; + let ptr: Address = SIG.scan_once(game, main_module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -39,7 +39,7 @@ impl State { }; self.cached_iwram_pointer = { - let ptr: Address = SIG2.scan_process_range(game, main_module_range)? + 3; + let ptr: Address = SIG2.scan_once(game, main_module_range)? + 3; let mut addr: Address = ptr + 0x4 + game.read::(ptr).ok()?; if game.read::(ptr + 10).ok()? == 0x48 { @@ -58,11 +58,11 @@ impl State { const SIG_RUNNING2: Signature<16> = Signature::new("48 8B 15 ?? ?? ?? ?? 31 C0 8B 12 85 D2 74 ?? 48"); - if let Some(ptr) = SIG_RUNNING.scan_process_range(game, main_module_range) { + if let Some(ptr) = SIG_RUNNING.scan_once(game, main_module_range) { let ptr = ptr + 2; ptr + 0x4 + game.read::(ptr).ok()? + 0x1 } else { - let ptr = SIG_RUNNING2.scan_process_range(game, main_module_range)? + 3; + let ptr = SIG_RUNNING2.scan_once(game, main_module_range)? + 3; let ptr = ptr + 0x4 + game.read::(ptr).ok()?; game.read::(ptr).ok()?.into() } @@ -76,11 +76,11 @@ impl State { const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF FF 03 00"); const SIG_OLD: Signature<12> = Signature::new("81 E6 FF FF 03 00 8B 15 ?? ?? ?? ??"); - if let Some(ptr) = SIG.scan_process_range(game, main_module_range) { + if let Some(ptr) = SIG.scan_once(game, main_module_range) { self.cached_ewram_pointer = game.read::(ptr + 1).ok()?.into(); self.cached_iwram_pointer = { const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00"); - let ptr = SIG2.scan_process_range(game, main_module_range)?; + let ptr = SIG2.scan_once(game, main_module_range)?; game.read::(ptr + 1).ok()?.into() }; @@ -91,8 +91,8 @@ impl State { Signature::new("8B 15 ?? ?? ?? ?? 31 C0 85 D2 74 ?? 0F"); let ptr = SIG - .scan_process_range(game, main_module_range) - .or_else(|| SIG_OLD.scan_process_range(game, main_module_range))?; + .scan_once(game, main_module_range) + .or_else(|| SIG_OLD.scan_once(game, main_module_range))?; game.read::(ptr + 2).ok()?.into() }; @@ -101,7 +101,7 @@ impl State { let iwram = game.read::(self.cached_iwram_pointer).ok()?; Some([ewram.into(), iwram.into()]) - } else if let Some(ptr) = SIG_OLD.scan_process_range(game, main_module_range) { + } else if let Some(ptr) = SIG_OLD.scan_once(game, main_module_range) { // This code is for very old versions of VisualBoyAdvance (1.8.0-beta 3) self.cached_ewram_pointer = game.read::(ptr + 8).ok()?.into(); self.cached_iwram_pointer = self.cached_ewram_pointer.add_signed(0x4); @@ -109,7 +109,7 @@ impl State { self.is_emulating = { const SIG_RUNNING: Signature<11> = Signature::new("8B 0D ?? ?? ?? ?? 85 C9 74 ?? 8A"); - let ptr = SIG_RUNNING.scan_process_range(game, main_module_range)? + 2; + let ptr = SIG_RUNNING.scan_once(game, main_module_range)? + 2; game.read::(ptr).ok()?.into() }; diff --git a/src/emulator/genesis/blastem.rs b/src/emulator/genesis/blastem.rs index b996af4..f5b0990 100644 --- a/src/emulator/genesis/blastem.rs +++ b/src/emulator/genesis/blastem.rs @@ -18,7 +18,7 @@ impl State { .contains(MemoryRangeFlags::WRITE) && m.size().unwrap_or_default() == 0x101000 }) - .find_map(|m| SIG.scan_process_range(game, m.range().ok()?))? + .find_map(|m| SIG.scan_once(game, m.range().ok()?))? + 11; let wram = game.read::(scanned_address).ok()?; diff --git a/src/emulator/genesis/fusion.rs b/src/emulator/genesis/fusion.rs index c1a0582..9036a4e 100644 --- a/src/emulator/genesis/fusion.rs +++ b/src/emulator/genesis/fusion.rs @@ -14,7 +14,7 @@ impl State { .filter(|(_, state)| matches!(state, super::State::Fusion(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let ptr = SIG.scan_process_range(game, main_module)? + 1; + let ptr = SIG.scan_once(game, main_module)? + 1; let addr = ptr + game.read::(ptr).ok()? as u64 + 3; let addr = game.read::(addr).ok()?; diff --git a/src/emulator/genesis/gens.rs b/src/emulator/genesis/gens.rs index c8440a5..2a3e847 100644 --- a/src/emulator/genesis/gens.rs +++ b/src/emulator/genesis/gens.rs @@ -12,7 +12,7 @@ impl State { .filter(|(_, state)| matches!(state, super::State::Gens(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let ptr = SIG.scan_process_range(game, main_module)? + 11; + let ptr = SIG.scan_once(game, main_module)? + 11; *endian = if game.read::(ptr + 4).ok()? == 0x86 { Endian::Big diff --git a/src/emulator/genesis/retroarch.rs b/src/emulator/genesis/retroarch.rs index eedfd22..75367c2 100644 --- a/src/emulator/genesis/retroarch.rs +++ b/src/emulator/genesis/retroarch.rs @@ -45,7 +45,7 @@ impl State { .contains(MemoryRangeFlags::WRITE) && m.size().unwrap_or_default() == 0x101000 }) - .find_map(|m| SIG.scan_process_range(game, m.range().ok()?))? + .find_map(|m| SIG.scan_once(game, m.range().ok()?))? + 11; let wram = game.read::(scanned_address).ok()?; @@ -58,10 +58,9 @@ impl State { if is_x86_64 { const SIG_64: Signature<10> = Signature::new("48 8D 0D ?? ?? ?? ?? 4C 8B 2D"); - let addr = SIG_64.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 3; + let addr = SIG_64 + .scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + + 3; let wram = addr + 0x4 + game.read::(addr).ok()?; @@ -69,10 +68,9 @@ impl State { } else { const SIG_32: Signature<7> = Signature::new("A3 ?? ?? ?? ?? 29 F9"); - let ptr = SIG_32.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 1; + let ptr = SIG_32 + .scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + + 1; let wram = game.read::(ptr).ok()?; @@ -85,10 +83,9 @@ impl State { if is_x86_64 { const SIG_64: Signature<9> = Signature::new("48 8D 0D ?? ?? ?? ?? 41 B8"); - let addr = SIG_64.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 3; + let addr = SIG_64 + .scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + + 3; let wram = addr + 0x4 + game.read::(addr).ok()?; @@ -96,10 +93,9 @@ impl State { } else { const SIG_32: Signature<8> = Signature::new("B9 ?? ?? ?? ?? C1 EF 10"); - let ptr = SIG_32.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 1; + let ptr = SIG_32 + .scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + + 1; let wram = game.read::(ptr).ok()?; diff --git a/src/emulator/genesis/segaclassics.rs b/src/emulator/genesis/segaclassics.rs index 49b4473..acf6df2 100644 --- a/src/emulator/genesis/segaclassics.rs +++ b/src/emulator/genesis/segaclassics.rs @@ -13,14 +13,14 @@ impl State { const GENESISWRAPPERDLL: &str = "GenesisEmuWrapper.dll"; let mut ptr = if let Ok(module) = game.get_module_range(GENESISWRAPPERDLL) { - SIG_GAMEROOM.scan_process_range(game, module)? + 2 + SIG_GAMEROOM.scan_once(game, module)? + 2 } else { let main_module = super::PROCESS_NAMES .iter() .filter(|(_, state)| matches!(state, super::State::SegaClassics(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - SIG_SEGACLASSICS.scan_process_range(game, main_module)? + 8 + SIG_SEGACLASSICS.scan_once(game, main_module)? + 8 }; ptr = game.read::(ptr).ok()?.into(); diff --git a/src/emulator/ps1/duckstation.rs b/src/emulator/ps1/duckstation.rs index 2967a4a..90c873f 100644 --- a/src/emulator/ps1/duckstation.rs +++ b/src/emulator/ps1/duckstation.rs @@ -24,7 +24,7 @@ impl State { self.addr = debug_symbol.address; } else { // For older versions of Duckstation, we fall back to regular sigscanning - let addr = SIG.scan_process_range(game, main_module_range)? + 3; + let addr = SIG.scan_once(game, main_module_range)? + 3; self.addr = addr + 0x4 + game.read::(addr).ok()?; } diff --git a/src/emulator/ps1/epsxe.rs b/src/emulator/ps1/epsxe.rs index d7461fc..bd23a16 100644 --- a/src/emulator/ps1/epsxe.rs +++ b/src/emulator/ps1/epsxe.rs @@ -12,7 +12,7 @@ impl State { .filter(|(_, state)| matches!(state, super::State::Epsxe(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let ptr = SIG.scan_process_range(game, main_module_range)? + 5; + let ptr = SIG.scan_once(game, main_module_range)? + 5; Some(game.read::(ptr).ok()?.into()) } diff --git a/src/emulator/ps1/mednafen.rs b/src/emulator/ps1/mednafen.rs index 9d351bb..e947c81 100644 --- a/src/emulator/ps1/mednafen.rs +++ b/src/emulator/ps1/mednafen.rs @@ -17,8 +17,8 @@ impl State { pe::MachineType::read(game, main_module_range.0) == Some(pe::MachineType::X86_64); let ptr = match is_64_bit { - true => SIG_64.scan_process_range(game, main_module_range)?, - false => SIG_32.scan_process_range(game, main_module_range)?, + true => SIG_64.scan_once(game, main_module_range)?, + false => SIG_32.scan_once(game, main_module_range)?, } + 0x5; Some(game.read::(ptr).ok()?.into()) diff --git a/src/emulator/ps1/pcsx_redux.rs b/src/emulator/ps1/pcsx_redux.rs index b193c0a..188c10b 100644 --- a/src/emulator/ps1/pcsx_redux.rs +++ b/src/emulator/ps1/pcsx_redux.rs @@ -25,10 +25,10 @@ impl State { ); const SIG_OFFSET: Signature<9> = Signature::new("89 D1 C1 E9 10 48 8B ?? ??"); - self.addr_base = SIG_BASE.scan_process_range(game, main_module_range)? + 2; + self.addr_base = SIG_BASE.scan_once(game, main_module_range)? + 2; self.addr = game.read::(self.addr_base).ok()?.into(); - let offset = SIG_OFFSET.scan_process_range(game, main_module_range)? + 8; + let offset = SIG_OFFSET.scan_once(game, main_module_range)? + 8; let offset = game.read::(offset).ok()? as u64; let addr = game.read::(self.addr + offset).ok()?; @@ -45,7 +45,7 @@ impl State { .unwrap_or_default() .contains(MemoryRangeFlags::WRITE) }) - .find_map(|m| SIG.scan_process_range(game, m.range().ok()?))? + .find_map(|m| SIG.scan_once(game, m.range().ok()?))? + 2; self.addr = game.read::(self.addr_base).ok()?.into(); diff --git a/src/emulator/ps1/psxfin.rs b/src/emulator/ps1/psxfin.rs index 73fc5f0..0d0c00b 100644 --- a/src/emulator/ps1/psxfin.rs +++ b/src/emulator/ps1/psxfin.rs @@ -15,14 +15,13 @@ impl State { .filter(|(_, state)| matches!(state, super::State::PsxFin(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let mut ptr: Address32 = if let Some(sig) = SIG.scan_process_range(game, main_module_range) - { + let mut ptr: Address32 = if let Some(sig) = SIG.scan_once(game, main_module_range) { game.read(sig + 2).ok()? - } else if let Some(sig) = SIG_0.scan_process_range(game, main_module_range) { + } else if let Some(sig) = SIG_0.scan_once(game, main_module_range) { game.read(sig + 1).ok()? - } else if let Some(sig) = SIG_1.scan_process_range(game, main_module_range) { + } else if let Some(sig) = SIG_1.scan_once(game, main_module_range) { game.read(sig + 1).ok()? - } else if let Some(sig) = SIG_2.scan_process_range(game, main_module_range) { + } else if let Some(sig) = SIG_2.scan_once(game, main_module_range) { game.read(sig + 1).ok()? } else { return None; diff --git a/src/emulator/ps1/retroarch.rs b/src/emulator/ps1/retroarch.rs index 50b7c42..8153154 100644 --- a/src/emulator/ps1/retroarch.rs +++ b/src/emulator/ps1/retroarch.rs @@ -33,18 +33,14 @@ impl State { if is_64_bit { const SIG: Signature<14> = Signature::new("48 8B 05 ?? ?? ?? ?? 41 81 E4 FF FF 1F 00"); - let ptr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 3; + let ptr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 3; let ptr = ptr + 0x4 + game.read::(ptr).ok()?; Some(game.read::(ptr).ok()?.into()) } else { const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 E3 FF FF 1F 00"); - let ptr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 1; + let ptr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 1; let ptr = game.read::(ptr).ok()?; Some(game.read::(ptr).ok()?.into()) } @@ -53,18 +49,14 @@ impl State { if is_64_bit { const SIG: Signature<15> = Signature::new("48 89 0D ?? ?? ?? ?? 89 35 ?? ?? ?? ?? 89 3D"); - let addr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 3; + let addr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 3; let ptr = addr + 0x4 + game.read::(addr).ok()?; Some(game.read::(ptr).ok()?.into()) } else { const SIG: Signature<8> = Signature::new("A1 ?? ?? ?? ?? 23 CB 8B"); - let ptr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 1; + let ptr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 1; let ptr = game.read::(ptr).ok()?; Some(game.read::(ptr).ok()?.into()) } @@ -72,19 +64,15 @@ impl State { // PCSX ReARMed if is_64_bit { const SIG: Signature<9> = Signature::new("48 8B 35 ?? ?? ?? ?? 81 E2"); - let addr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 3; + let addr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 3; let ptr = addr + 0x4 + game.read::(addr).ok()?; let ptr = game.read::(ptr).ok()?; Some(game.read::(ptr).ok()?.into()) } else { const SIG: Signature<9> = Signature::new("FF FF 1F 00 89 ?? ?? ?? A1"); - let ptr = SIG.scan_process_range( - game, - (core_address, game.get_module_size(core_name).ok()?), - )? + 9; + let ptr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 9; let ptr = game.read::(ptr).ok()?; Some(game.read::(ptr).ok()?.into()) } diff --git a/src/emulator/ps1/xebra.rs b/src/emulator/ps1/xebra.rs index 5cbae53..78f5066 100644 --- a/src/emulator/ps1/xebra.rs +++ b/src/emulator/ps1/xebra.rs @@ -12,7 +12,7 @@ impl State { .filter(|(_, state)| matches!(state, super::State::Xebra(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let ptr = SIG.scan_process_range(game, main_module_range)? + 1; + let ptr = SIG.scan_once(game, main_module_range)? + 1; let addr = ptr + 0x4 + game.read::(ptr).ok()?; let addr = game.read::(addr + 0x16A).ok()?; let addr = game.read::(addr).ok()?; diff --git a/src/emulator/ps2/pcsx2.rs b/src/emulator/ps2/pcsx2.rs index 96b36ab..88f754f 100644 --- a/src/emulator/ps2/pcsx2.rs +++ b/src/emulator/ps2/pcsx2.rs @@ -18,15 +18,15 @@ impl State { self.addr_base = if self.is_64_bit { const SIG: Signature<12> = Signature::new("48 8B ?? ?? ?? ?? ?? 25 F0 3F 00 00"); - let ptr = SIG.scan_process_range(game, main_module_range)? + 3; + let ptr = SIG.scan_once(game, main_module_range)? + 3; ptr + 0x4 + game.read::(ptr).ok()? } else { const SIG: Signature<11> = Signature::new("8B ?? ?? ?? ?? ?? 25 F0 3F 00 00"); const SIG_ALT: Signature<12> = Signature::new("8B ?? ?? ?? ?? ?? 81 ?? F0 3F 00 00"); - let ptr = if let Some(addr) = SIG.scan_process_range(game, main_module_range) { + let ptr = if let Some(addr) = SIG.scan_once(game, main_module_range) { addr + 2 } else { - SIG_ALT.scan_process_range(game, main_module_range)? + 2 + SIG_ALT.scan_once(game, main_module_range)? + 2 }; self.read_pointer(game, ptr).ok()? }; diff --git a/src/emulator/ps2/retroarch.rs b/src/emulator/ps2/retroarch.rs index 5b38c76..61a8581 100644 --- a/src/emulator/ps2/retroarch.rs +++ b/src/emulator/ps2/retroarch.rs @@ -31,9 +31,8 @@ impl State { let base_addr = { const SIG: Signature<13> = Signature::new("48 8B ?? ?? ?? ?? ?? 81 ?? F0 3F 00 00"); - let ptr = SIG - .scan_process_range(game, (core_address, game.get_module_size(core_name).ok()?))? - + 3; + let ptr = + SIG.scan_once(game, (core_address, game.get_module_size(core_name).ok()?))? + 3; ptr + 0x4 + game.read::(ptr).ok()? }; diff --git a/src/emulator/sms/blastem.rs b/src/emulator/sms/blastem.rs index 5a00aef..ec6281c 100644 --- a/src/emulator/sms/blastem.rs +++ b/src/emulator/sms/blastem.rs @@ -15,7 +15,7 @@ impl State { .contains(MemoryRangeFlags::WRITE) && m.size().unwrap_or_default() == 0x101000 }) - .find_map(|m| SIG.scan_process_range(game, m.range().ok()?))? + .find_map(|m| SIG.scan_once(game, m.range().ok()?))? + 10; let wram: Address = game.read::(scanned_address).ok()?.into(); diff --git a/src/emulator/sms/fusion.rs b/src/emulator/sms/fusion.rs index da4b7c0..2e58414 100644 --- a/src/emulator/sms/fusion.rs +++ b/src/emulator/sms/fusion.rs @@ -14,7 +14,7 @@ impl State { .filter(|(_, state)| matches!(state, super::State::Fusion(_))) .find_map(|(name, _)| game.get_module_range(name).ok())?; - let ptr = SIG.scan_process_range(game, main_module)? + 4; + let ptr = SIG.scan_once(game, main_module)? + 4; self.addr = game.read::(ptr).ok()?.into(); Some(game.read::(self.addr).ok()?.add(0xC000).into()) diff --git a/src/emulator/sms/mednafen.rs b/src/emulator/sms/mednafen.rs index 569359d..46f139f 100644 --- a/src/emulator/sms/mednafen.rs +++ b/src/emulator/sms/mednafen.rs @@ -17,8 +17,8 @@ impl State { pe::MachineType::read(game, main_module_range.0) == Some(pe::MachineType::X86_64); let ptr = match is_64_bit { - true => SIG_64.scan_process_range(game, main_module_range)? + 8, - false => SIG_32.scan_process_range(game, main_module_range)? + 7, + true => SIG_64.scan_once(game, main_module_range)? + 8, + false => SIG_32.scan_once(game, main_module_range)? + 7, }; Some(game.read::(ptr).ok()?.into()) diff --git a/src/emulator/sms/retroarch.rs b/src/emulator/sms/retroarch.rs index 81db41d..1858a7a 100644 --- a/src/emulator/sms/retroarch.rs +++ b/src/emulator/sms/retroarch.rs @@ -48,11 +48,11 @@ impl State { Some( if is_64_bit { const SIG: Signature<9> = Signature::new("48 8D 0D ?? ?? ?? ?? 41 B8"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 3; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 3; ptr + 0x4 + game.read::(ptr).ok()? } else { const SIG: Signature<8> = Signature::new("B9 ?? ?? ?? ?? C1 EF 10"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 1; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 1; game.read::(ptr).ok()?.into() } + 0x20000, ) @@ -63,11 +63,11 @@ impl State { Some(if is_64_bit { const SIG: Signature<10> = Signature::new("48 8D 0D ?? ?? ?? ?? 4C 8B 2D"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 3; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 3; ptr + 0x4 + game.read::(ptr).ok()? } else { const SIG: Signature<7> = Signature::new("A3 ?? ?? ?? ?? 29 F9"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 1; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 1; game.read::(ptr).ok()?.into() }) } @@ -77,11 +77,11 @@ impl State { Some(if is_64_bit { const SIG: Signature<5> = Signature::new("31 F6 48 C7 05"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 5; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 5; ptr + 0x8 + game.read::(ptr).ok()? } else { const SIG: Signature<4> = Signature::new("83 FA 02 B8"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 4; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 4; game.read::(ptr).ok()?.into() }) } @@ -91,7 +91,7 @@ impl State { Some(if is_64_bit { const SIG: Signature<13> = Signature::new("83 ?? 02 75 ?? 48 8B 0D ?? ?? ?? ?? E8"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 8; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 8; let offset = game .read::(ptr + 13 + 0x4 + game.read::(ptr + 13).ok()? + 3) .ok()?; @@ -109,7 +109,7 @@ impl State { } } else { const SIG: Signature<12> = Signature::new("83 ?? 02 75 ?? 8B ?? ?? ?? ?? ?? E8"); - let ptr = SIG.scan_process_range(game, (self.core_base, module_size))? + 7; + let ptr = SIG.scan_once(game, (self.core_base, module_size))? + 7; let offset = game .read::(ptr + 12 + 0x4 + game.read::(ptr + 12).ok()? + 2) .ok()?; diff --git a/src/future/mod.rs b/src/future/mod.rs index a9aa26a..73b93a8 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -109,8 +109,6 @@ use core::{ task::{Context, Poll}, }; -#[cfg(feature = "signature")] -use crate::signature::Signature; use crate::{Address, Process}; #[cfg(target_os = "wasi")] @@ -254,22 +252,6 @@ impl Process { } } -#[cfg(feature = "signature")] -impl Signature { - /// Asynchronously awaits scanning a process for the signature until it is - /// found. This will scan the address range of the process given. Once the - /// signature is found, the address of the start of the signature is - /// returned. - pub async fn wait_scan_process_range( - &self, - process: &Process, - (addr, len): (impl Into
, u64), - ) -> Address { - let addr = addr.into(); - retry(|| self.scan_process_range(process, (addr, len))).await - } -} - /// A future that executes a future until the process closes. #[must_use = "You need to await this future."] pub struct UntilProcessCloses<'a, F> { diff --git a/src/game_engine/unity/il2cpp.rs b/src/game_engine/unity/il2cpp.rs index a570d73..ca078bc 100644 --- a/src/game_engine/unity/il2cpp.rs +++ b/src/game_engine/unity/il2cpp.rs @@ -59,14 +59,14 @@ impl Module { const ASSEMBLIES_TRG_SIG: Signature<12> = Signature::new("48 FF C5 80 3C ?? 00 75 ?? 48 8B 1D"); - let addr = ASSEMBLIES_TRG_SIG.scan_process_range(process, mono_module)? + 12; + let addr = ASSEMBLIES_TRG_SIG.scan_once(process, mono_module)? + 12; addr + 0x4 + process.read::(addr).ok()? } PointerSize::Bit32 => { const ASSEMBLIES_TRG_SIG: Signature<9> = Signature::new("8A 07 47 84 C0 75 ?? 8B 35"); - let addr = ASSEMBLIES_TRG_SIG.scan_process_range(process, mono_module)? + 9; + let addr = ASSEMBLIES_TRG_SIG.scan_once(process, mono_module)? + 9; process.read_pointer(addr, pointer_size).ok()? } _ => return None, @@ -77,7 +77,7 @@ impl Module { Signature::new("48 83 3C ?? 00 75 ?? 8B C? E8"); let addr = TYPE_INFO_DEFINITION_TABLE_TRG_SIG - .scan_process_range(process, mono_module)? + .scan_once(process, mono_module)? .add_signed(-4); process @@ -88,8 +88,7 @@ impl Module { const TYPE_INFO_DEFINITION_TABLE_TRG_SIG: Signature<10> = Signature::new("C3 A1 ?? ?? ?? ?? 83 3C ?? 00"); - let addr = - TYPE_INFO_DEFINITION_TABLE_TRG_SIG.scan_process_range(process, mono_module)? + 2; + let addr = TYPE_INFO_DEFINITION_TABLE_TRG_SIG.scan_once(process, mono_module)? + 2; process .read_pointer(process.read_pointer(addr, pointer_size).ok()?, pointer_size) @@ -363,7 +362,7 @@ impl Class { &'a self, process: &'a Process, module: &'a Module, - ) -> impl FusedIterator + '_ { + ) -> impl FusedIterator + 'a { let mut this_class = Some(*self); iter::from_fn(move || { @@ -799,14 +798,14 @@ fn detect_version(process: &Process) -> Option { const SIG_202X: Signature<6> = Signature::new("00 32 30 32 ?? 2E"); const SIG_2019: Signature<6> = Signature::new("00 32 30 31 39 2E"); - if SIG_202X.scan_process_range(process, unity_module).is_some() { + if SIG_202X.scan_once(process, unity_module).is_some() { let il2cpp_version = { const SIG: Signature<14> = Signature::new("48 2B ?? 48 2B ?? ?? ?? ?? ?? 48 F7 ?? 48"); let address = process.get_module_address("GameAssembly.dll").ok()?; let size = pe::read_size_of_image(process, address)? as u64; let ptr = { - let addr = SIG.scan_process_range(process, (address, size))? + 6; + let addr = SIG.scan_once(process, (address, size))? + 6; addr + 0x4 + process.read::(addr).ok()? }; @@ -819,7 +818,7 @@ fn detect_version(process: &Process) -> Option { } else { Version::V2019 }) - } else if SIG_2019.scan_process_range(process, unity_module).is_some() { + } else if SIG_2019.scan_once(process, unity_module).is_some() { Some(Version::V2019) } else { Some(Version::Base) diff --git a/src/game_engine/unity/mono.rs b/src/game_engine/unity/mono.rs index 0e9857e..b65aca9 100644 --- a/src/game_engine/unity/mono.rs +++ b/src/game_engine/unity/mono.rs @@ -62,9 +62,8 @@ impl Module { let assemblies: Address = match pointer_size { PointerSize::Bit64 => { const SIG_MONO_64: Signature<3> = Signature::new("48 8B 0D"); - let scan_address: Address = SIG_MONO_64 - .scan_process_range(process, (root_domain_function_address, 0x100))? - + 3; + let scan_address: Address = + SIG_MONO_64.scan_once(process, (root_domain_function_address, 0x100))? + 3; scan_address + 0x4 + process.read::(scan_address).ok()? } PointerSize::Bit32 => { @@ -72,7 +71,7 @@ impl Module { const SIG_32_2: Signature<2> = Signature::new("8B 0D"); let ptr = [SIG_32_1, SIG_32_2].iter().find_map(|sig| { - sig.scan_process_range(process, (root_domain_function_address, 0x100)) + sig.scan_once(process, (root_domain_function_address, 0x100)) })? + 2; process.read::(ptr).ok()?.into() @@ -980,7 +979,7 @@ fn detect_version(process: &Process) -> Option { const SIG_202X: Signature<6> = Signature::new("00 32 30 32 ?? 2E"); - let Some(addr) = SIG_202X.scan_process_range(process, unity_module) else { + let Some(addr) = SIG_202X.scan_once(process, unity_module) else { return Some(Version::V2); }; diff --git a/src/game_engine/unity/scene.rs b/src/game_engine/unity/scene.rs index 72625c2..0025808 100644 --- a/src/game_engine/unity/scene.rs +++ b/src/game_engine/unity/scene.rs @@ -56,13 +56,13 @@ impl SceneManager { // There are multiple signatures that can be used, depending on the version of Unity // used in the target game. let base_address: Address = if pointer_size == PointerSize::Bit64 { - let addr = SIG_64_BIT.scan_process_range(process, unity_player)? + 7; + let addr = SIG_64_BIT.scan_once(process, unity_player)? + 7; addr + 0x4 + process.read::(addr).ok()? - } else if let Some(addr) = SIG_32_1.scan_process_range(process, unity_player) { + } else if let Some(addr) = SIG_32_1.scan_once(process, unity_player) { process.read::(addr + 5).ok()?.into() - } else if let Some(addr) = SIG_32_2.scan_process_range(process, unity_player) { + } else if let Some(addr) = SIG_32_2.scan_once(process, unity_player) { process.read::(addr.add_signed(-4)).ok()?.into() - } else if let Some(addr) = SIG_32_3.scan_process_range(process, unity_player) { + } else if let Some(addr) = SIG_32_3.scan_once(process, unity_player) { process.read::(addr + 7).ok()?.into() } else { return None; diff --git a/src/game_engine/unreal/mod.rs b/src/game_engine/unreal/mod.rs index dfb7518..f93905b 100644 --- a/src/game_engine/unreal/mod.rs +++ b/src/game_engine/unreal/mod.rs @@ -49,9 +49,9 @@ impl Module { (Signature::new("A8 01 75 ?? C7 05 ??"), 6), ]; - let addr = GENGINE.iter().find_map(|(sig, offset)| { - Some(sig.scan_process_range(process, module_range)? + *offset) - })?; + let addr = GENGINE + .iter() + .find_map(|(sig, offset)| Some(sig.scan_once(process, module_range)? + *offset))?; addr + 0x8 + process.read::(addr).ok()? }; @@ -71,9 +71,9 @@ impl Module { ), ]; - let addr = GWORLD.iter().find_map(|(sig, offset)| { - Some(sig.scan_process_range(process, module_range)? + *offset) - })?; + let addr = GWORLD + .iter() + .find_map(|(sig, offset)| Some(sig.scan_once(process, module_range)? + *offset))?; addr + 0x4 + process.read::(addr).ok()? }; @@ -84,9 +84,9 @@ impl Module { (Signature::new("57 0F B7 F8 74 ?? B8 ?? ?? ?? ?? 8B 44"), 7), ]; - let addr = FNAME_POOL.iter().find_map(|(sig, offset)| { - Some(sig.scan_process_range(process, module_range)? + *offset) - })?; + let addr = FNAME_POOL + .iter() + .find_map(|(sig, offset)| Some(sig.scan_once(process, module_range)? + *offset))?; addr + 0x4 + process.read::(addr).ok()? }; diff --git a/src/signature.rs b/src/signature.rs index cc11593..f4a2a51 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -1,18 +1,18 @@ //! Support for finding patterns in a process's memory. -use core::mem::{self, MaybeUninit}; - -use bytemuck::AnyBitPattern; - -use crate::{Address, Process}; +use crate::{future::retry, Address, Process}; +use core::{ + iter, + mem::{self, MaybeUninit}, + slice, +}; type Offset = u8; -/// A signature that can be used to find a pattern in a process. It is -/// recommended to store this in a `static` or `const` variable to ensure that -/// the signature is parsed at compile time, which enables the code to be -/// optimized a lot more. Additionally it is recommend to compile the code with -/// the `simd128` target feature to enable the use of SIMD instructions. +/// A signature that can be used to find a pattern in a process's memory. +/// It is recommended to store this in a `static` or `const` variable to ensure +/// that the signature is parsed at compile time, which allows optimizations. +/// Also, compiling with the `simd128` feature is recommended for SIMD support. #[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum Signature { @@ -29,6 +29,7 @@ pub enum Signature { }, } +/// A helper struct to parse a hexadecimal signature string into bytes. struct Parser<'a> { bytes: &'a [u8], } @@ -41,12 +42,12 @@ impl Parser<'_> { let b: u8 = *b; return ( Some(match b { - b'0'..=b'9' => b - b'0', - b'a'..=b'f' => b - b'a' + 0xA, - b'A'..=b'F' => b - b'A' + 0xA, - b'?' => 0x10, - b' ' | b'\r' | b'\n' | b'\t' => continue, - _ => panic!("Invalid byte"), + b'0'..=b'9' => b - b'0', // Convert '0'-'9' to their numeric value + b'a'..=b'f' => b - b'a' + 0xA, // Convert 'a'-'f' to their numeric value + b'A'..=b'F' => b - b'A' + 0xA, // Convert 'A'-'F' to their numeric value + b'?' => 0x10, // Treat wildcard ('?') as a special byte + b' ' | b'\r' | b'\n' | b'\t' => continue, // Skip whitespace + _ => panic!("Invalid byte"), // Invalid characters cause a panic }), self, ); @@ -55,6 +56,7 @@ impl Parser<'_> { } } +/// Checks if a slice of bytes contains the specified `search_byte`. #[inline] const fn contains(mut bytes: &[u8], search_byte: u8) -> bool { while let [b, rem @ ..] = bytes { @@ -69,13 +71,13 @@ const fn contains(mut bytes: &[u8], search_byte: u8) -> bool { impl Signature { /// Creates a new signature from a string. The string must be a hexadecimal /// string with `?` as wildcard. It is recommended to store this in a - /// `static` or `const` variable to ensure that the signature is parsed at - /// compile time, which enables the code to be optimized a lot more. + /// `static` or `const` variable to ensure that the signature is parted + /// at compile time, which allows optimizations. /// /// # Panics /// - /// This function panics if the signature is invalid. It also panics if the - /// signature is longer than 255 bytes. + /// This function panics if the signature is invalid or if its length + /// exceeds 255 bytes. /// /// # Example /// @@ -92,6 +94,7 @@ impl Signature { bytes: signature.as_bytes(), }; + // Check if the signature contains wildcards if contains(signature.as_bytes(), b'?') { let mut needle = [0; N]; let mut mask = [0; N]; @@ -109,13 +112,15 @@ impl Signature { mask[i] = mask_byte; i += 1; } - assert!(i == N); + assert!(i == N); // Ensure we parsed the correct number of bytes + // Initialize skip_offsets with all zeros let mut skip_offsets = [0; 256]; let mut unknown = 0; let end = N - 1; let mut i = 0; + while i < end { let byte = needle[i]; let mask = mask[i]; @@ -131,6 +136,7 @@ impl Signature { unknown = N as Offset; } + // Set the skip offsets for any byte that wasn't explicitly set i = 0; while i < skip_offsets.len() { if unknown < skip_offsets[i] || skip_offsets[i] == 0 { @@ -145,6 +151,7 @@ impl Signature { skip_offsets, } } else { + // If the provided string has no wildcards, treat as a Simple signature let mut needle = [0; N]; let mut i = 0; @@ -164,136 +171,278 @@ impl Signature { } } - fn scan(&self, haystack: &[u8]) -> Option { - match self { - Signature::Simple(needle) => memchr::memmem::find(haystack, needle), - Signature::Complex { - needle, - mask, - skip_offsets, - } => { - let mut current = 0; - let end = N - 1; - while let Some(scan) = strip_pod::<[u8; N]>(&mut &haystack[current..]) { - if matches(scan, needle, mask) { - return Some(current); + /// Performs a signature scan over a provided slice. + /// Returns an iterator over the positions where the signature matches. + fn scan_internal<'a>(&'a self, haystack: &'a [u8]) -> impl Iterator + 'a { + let mut cursor = 0; + let end = haystack.len().saturating_sub(N.saturating_sub(1)); + + iter::from_fn(move || 'outer: loop { + if cursor >= end { + return None; + } + + match self { + Signature::Simple(needle) => { + match memchr::memmem::find(&haystack[cursor..], needle) { + Some(offset) => { + let current_cursor = cursor; + cursor += offset + 1; + return Some(offset + current_cursor); + } + None => return None, + }; + } + Signature::Complex { + needle, + mask, + skip_offsets, + } => { + let mut i = 0; + + unsafe { + let (scan, mut needle, mut mask) = ( + haystack.as_ptr().add(cursor), + needle.as_ptr(), + mask.as_ptr(), + ); + + #[cfg(target_feature = "simd128")] + while i + 16 <= N { + use core::arch::wasm32::{u8x16_ne, v128, v128_and, v128_any_true}; + + if v128_any_true(u8x16_ne( + v128_and( + scan.add(i).cast::().read_unaligned(), + mask.cast::().read_unaligned(), + ), + needle.cast::().read_unaligned(), + )) { + cursor += + skip_offsets[*scan.add(N.saturating_sub(1)) as usize] as usize; + continue 'outer; + } else { + mask = mask.add(16); + needle = needle.add(16); + i += 16; + } + } + + while i + 8 <= N { + if scan.add(i).cast::().read_unaligned() + & mask.cast::().read_unaligned() + != needle.cast::().read_unaligned() + { + cursor += + skip_offsets[*scan.add(N.saturating_sub(1)) as usize] as usize; + continue 'outer; + } else { + mask = mask.add(8); + needle = needle.add(8); + i += 8; + } + } + + while i + 4 <= N { + if scan.add(i).cast::().read_unaligned() + & mask.cast::().read_unaligned() + != needle.cast::().read_unaligned() + { + cursor += + skip_offsets[*scan.add(N.saturating_sub(1)) as usize] as usize; + continue 'outer; + } else { + mask = mask.add(4); + needle = needle.add(4); + i += 4; + } + } + + while i + 2 <= N { + if scan.add(i).cast::().read_unaligned() + & mask.cast::().read_unaligned() + != needle.cast::().read_unaligned() + { + cursor += + skip_offsets[*scan.add(N.saturating_sub(1)) as usize] as usize; + continue 'outer; + } else { + mask = mask.add(2); + needle = needle.add(2); + i += 2; + } + } + + while i < N { + if *scan.add(i) & *mask != *needle { + cursor += + skip_offsets[*scan.add(N.saturating_sub(1)) as usize] as usize; + continue 'outer; + } else { + mask = mask.add(1); + needle = needle.add(1); + i += 1; + } + } + + let current_cursor = cursor; + cursor = cursor + 1; + return Some(current_cursor); } - let offset = skip_offsets[scan[end] as usize]; - current += offset as usize; } - None } - } + }) + .fuse() } - /// Scans a process for the signature. This will scan the address range of - /// the process given. If the signature is found, the address of the start - /// of the signature is returned. - pub fn scan_process_range( - &self, - process: &Process, - (addr, len): (impl Into
, u64), + /// Scans a process's memory in the given range for the first occurrence of the signature. + /// + /// # Arguments + /// + /// * `process` - A reference to the `Process` in which the scan occurs. + /// * `range` - A tuple containing: + /// - The starting address of the memory range + /// - The length of the memory range to scan + /// + /// Returns `Some(Address)` of the first match if found, otherwise `None`. + pub fn scan_once<'a>( + &'a self, + process: &'a Process, + range: (impl Into
, u64), ) -> Option
{ - let mut addr: Address = Into::into(addr); - // TODO: Handle the case where a signature may be cut in half by a page - // boundary. - let overall_end = addr.value() + len; - let mut buf = [MaybeUninit::uninit(); 4 << 10]; - while addr.value() < overall_end { - // We round up to the 4 KiB address boundary as that's a single - // page, which is safe to read either fully or not at all. We do - // this to do a single read rather than many small ones as the - // syscall overhead is a quite high. - let end = (addr.value() & !((4 << 10) - 1)) + (4 << 10).min(overall_end); - let len = end - addr.value(); - let current_read_buf = &mut buf[..len as usize]; - if let Ok(current_read_buf) = process.read_into_uninit_buf(addr, current_read_buf) { - if let Some(pos) = self.scan(current_read_buf) { - return Some(addr.add(pos as u64)); - } - }; - addr = Address::new(end); - } - None + self.scan_iter(process, range).next() } -} -fn matches(scan: &[u8; N], needle: &[u8; N], mask: &[u8; N]) -> bool { - // SAFETY: Before reading individual chunks from the arrays, we check that - // we can still read values of that size. We also read them unaligned as the - // original arrays are entirely unaligned. - unsafe { - let mut i = 0; - let (mut scan, mut needle, mut mask) = (scan.as_ptr(), needle.as_ptr(), mask.as_ptr()); - #[cfg(target_feature = "simd128")] - while i + 16 <= N { - use core::arch::wasm32::{u8x16_ne, v128, v128_and, v128_any_true}; - - if v128_any_true(u8x16_ne( - v128_and( - scan.cast::().read_unaligned(), - mask.cast::().read_unaligned(), - ), - needle.cast::().read_unaligned(), - )) { - return false; - } - scan = scan.add(16); - mask = mask.add(16); - needle = needle.add(16); - i += 16; - } - while i + 8 <= N { - if scan.cast::().read_unaligned() & mask.cast::().read_unaligned() - != needle.cast::().read_unaligned() - { - return false; - } - scan = scan.add(8); - mask = mask.add(8); - needle = needle.add(8); - i += 8; + /// Returns an iterator over all occurrences of the signature in the process's memory range. + /// + /// # Arguments + /// + /// * `process` - A reference to the `Process` in which the scan occurs. + /// * `range` - A tuple containing: + /// - The starting address of the memory range + /// - The length of the memory range to scan + /// + /// Returns an iterator that yields each matching address. + pub fn scan_iter<'a>( + &'a self, + process: &'a Process, + range: (impl Into
, u64), + ) -> impl Iterator + 'a { + const MEM_SIZE: usize = 0x1000; + + let mut addr: Address = Into::into(range.0); + let overall_end = addr.value() + range.1; + + // The sigscan essentially works by reading one memory page (0x1000 bytes) + // at a time and looking for the signature in each page. We will create a buffer + // sligthly larger than 0x1000 bytes in order to accomodate the size of + // the memory page + the signature - 1. The very first bytes of the + // buffer are intended to be used as the tail of the previous memory page. + // This allows to scan across the memory page boundaries. + + // We should use N - 1 but we resort to MEM_SIZE - 1 to avoid using [feature(generic_const_exprs)] + #[repr(packed)] + struct Buffer { + _head: [u8; N], + _buffer: [u8; MEM_SIZE - 1], } - while i + 4 <= N { - if scan.cast::().read_unaligned() & mask.cast::().read_unaligned() - != needle.cast::().read_unaligned() - { - return false; + + // The tail of the previous memory page, if read correctly, is stored here + let mut tail = [0; N]; + let mut last_page_success = false; + + iter::from_fn(move || { + if addr.value() >= overall_end { + return None; } - scan = scan.add(4); - mask = mask.add(4); - needle = needle.add(4); - i += 4; - } - while i + 2 <= N { - if scan.cast::().read_unaligned() & mask.cast::().read_unaligned() - != needle.cast::().read_unaligned() - { - return false; + + let mut global_buffer = MaybeUninit::>::uninit(); + + let buf = { + // SAFETY: The buffer is not initialized, but we are returning a slice of MaybeUninit, which do not require initialization + unsafe { + slice::from_raw_parts_mut( + &mut global_buffer as *mut _ as *mut MaybeUninit, + size_of::>(), + ) + } + }; + + // We round up to the 4 KiB address boundary as that's a single + // page, which is safe to read either fully or not at all. We do + // this to reduce the number of syscalls as much as possible, as the + // syscall overhead is quite high. + let end = ((addr.value() & !((4 << 10) - 1)) + (4 << 10)).min(overall_end); + let len = end.saturating_sub(addr.value()) as usize; + + // If we read the previous memory page successfully, then we can copy the last + // elements to the start of the buffer. + if last_page_success { + unsafe { + (buf.as_mut_ptr() as *mut u8).copy_from(tail.as_ptr(), tail.len() - 1); + } } - scan = scan.add(2); - mask = mask.add(2); - needle = needle.add(2); - i += 2; - } - while i < N { - if *scan & *mask != *needle { - return false; + + let current_page_success = process + .read_into_uninit_buf(addr, &mut buf[N - 1..][..len]) + .is_ok(); + + // We define the final slice on which to perform the memory scan into. If we failed to read the memory page, + // this returns an empty slice so the subsequent iterator will result into an empty iterator. + // If we managed to read the current memory page, instead, we check if we have the data from the previous + // memory page if it got read successfully. + let scan_buf = unsafe { + let ptr = if current_page_success { + if last_page_success { + &buf[..len + N - 1] + } else { + &buf[N - 1..][..len] + } + } else { + &[] + }; + + mem::transmute::<&[MaybeUninit], &[u8]>(ptr) + }; + + if current_page_success { + (&mut tail[..N - 1]).copy_from_slice(&scan_buf[scan_buf.len() - (N - 1)..]); } - scan = scan.add(1); - mask = mask.add(1); - needle = needle.add(1); - i += 1; - } - true + + let cur_addr = addr; + let cur_suc = last_page_success; + + addr = Address::new(end); + last_page_success = current_page_success; + + Some(self.scan_internal(&scan_buf).map(move |pos| { + let mut address = cur_addr.add(pos as u64); + + if cur_suc { + address = address.add_signed(-(N as i64 - 1)) + } + + address + })) + }) + .flatten() } -} -fn strip_pod<'a, T: AnyBitPattern>(cursor: &mut &'a [u8]) -> Option<&'a T> { - if cursor.len() < mem::size_of::() { - return None; + /// Asynchronously awaits scanning a process for the signature until a match + /// is found. + /// + /// # Arguments + /// + /// * `process` - A reference to the `Process` in which the scan occurs. + /// * `range` - A tuple containing: + /// - The starting address of the memory range + /// - The length of the memory range to scan + pub async fn wait_scan_once( + &self, + process: &Process, + range: (impl Into
, u64), + ) -> Address { + let addr = range.0.into(); + retry(|| self.scan_iter(process, (addr, range.1)).next()).await } - let (before, after) = cursor.split_at(mem::size_of::()); - *cursor = after; - Some(bytemuck::from_bytes(before)) }