Skip to content

Commit 204e676

Browse files
committed
refactor: 统一游戏映像补丁的早期执行阶段
将游戏映像修改拆分为 TLS 前和原始入口前两个阶段,移除晚期重复执行,并在入口前统一安装 IAT hook。 补发 TLS 前补丁日志;将自定义版本、免费游玩文字、计时器、曲数上限和快速重启等纯内存修改纳入配置控制的入口前管线。 修正共享音频的 PUSH 参数位点并补充 2.45、2.47、2.50 偏移;修正 2.50 AppUser 实际使用的第二检查位点,同时保留旧版本位点和未知版本 AOB 回退。 补充 15 项早期补丁回归测试,覆盖阶段边界、版本位点、共享音频、计时器及其他迁移功能。
1 parent 45d80e4 commit 204e676

17 files changed

Lines changed: 452 additions & 240 deletions

src/early_patch.rs

Lines changed: 34 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
use std::ffi::c_void;
2+
use std::sync::Mutex;
23

34
use windows_sys_loader::Win32::System::Diagnostics::Debug::FlushInstructionCache;
45
use windows_sys_loader::Win32::System::Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE};
56
use windows_sys_loader::Win32::System::Threading::GetCurrentProcess;
67

78
use crate::config::Config;
89
use crate::patches;
10+
use crate::util::api::Api;
911
use crate::util::memory::PatchMemory;
1012

13+
enum EarlyLog {
14+
Info(String),
15+
Warn(String),
16+
}
17+
18+
static EARLY_LOGS: Mutex<Vec<EarlyLog>> = Mutex::new(Vec::new());
19+
1120
struct DirectMemory {
1221
game_base: usize,
1322
game_size: u32,
@@ -30,7 +39,20 @@ pub unsafe fn apply(game_base: usize) {
3039
game_size,
3140
};
3241
let config = Config::load(&base_dir);
33-
patches::apply_early(&memory, &config);
42+
patches::apply_pre_tls(&memory, &config);
43+
}
44+
45+
pub fn flush_logs(api: &Api) {
46+
let logs = match EARLY_LOGS.lock() {
47+
Ok(mut logs) => std::mem::take(&mut *logs),
48+
Err(_) => return,
49+
};
50+
for log in logs {
51+
match log {
52+
EarlyLog::Info(message) => api.log_info(&message),
53+
EarlyLog::Warn(message) => api.log_warn(&message),
54+
}
55+
}
3456
}
3557

3658
impl DirectMemory {
@@ -112,9 +134,17 @@ impl PatchMemory for DirectMemory {
112134
true
113135
}
114136

115-
fn log_info(&self, _message: &str) {}
137+
fn log_info(&self, message: &str) {
138+
if let Ok(mut logs) = EARLY_LOGS.lock() {
139+
logs.push(EarlyLog::Info(message.to_owned()));
140+
}
141+
}
116142

117-
fn log_warn(&self, _message: &str) {}
143+
fn log_warn(&self, message: &str) {
144+
if let Ok(mut logs) = EARLY_LOGS.lock() {
145+
logs.push(EarlyLog::Warn(message.to_owned()));
146+
}
147+
}
118148
}
119149

120150
fn find_pattern(image: &[u8], pattern: &[u8], mask: &str) -> Option<usize> {
@@ -132,88 +162,4 @@ fn find_pattern(image: &[u8], pattern: &[u8], mask: &str) -> Option<usize> {
132162
}
133163

134164
#[cfg(test)]
135-
mod tests {
136-
use std::cell::RefCell;
137-
138-
use super::*;
139-
140-
struct FakeMemory {
141-
base: usize,
142-
image: RefCell<Vec<u8>>,
143-
}
144-
145-
impl PatchMemory for FakeMemory {
146-
fn game_base(&self) -> usize {
147-
self.base
148-
}
149-
150-
fn game_size(&self) -> u32 {
151-
self.image.borrow().len() as u32
152-
}
153-
154-
fn aob_scan(&self, start: usize, size: u32, pattern: &[u8], mask: &str) -> usize {
155-
let offset = start - self.base;
156-
let image = self.image.borrow();
157-
find_pattern(&image[offset..offset + size as usize], pattern, mask)
158-
.map_or(0, |found| start + found)
159-
}
160-
161-
fn mem_read(&self, addr: usize, buf: &mut [u8]) -> bool {
162-
let offset = addr - self.base;
163-
buf.copy_from_slice(&self.image.borrow()[offset..offset + buf.len()]);
164-
true
165-
}
166-
167-
fn mem_write(&self, addr: usize, data: &[u8]) -> bool {
168-
let offset = addr - self.base;
169-
self.image.borrow_mut()[offset..offset + data.len()].copy_from_slice(data);
170-
true
171-
}
172-
173-
fn log_info(&self, _message: &str) {}
174-
175-
fn log_warn(&self, _message: &str) {}
176-
}
177-
178-
#[test]
179-
fn early_scanner_matches_wildcards_before_tls() {
180-
// Given: 版本间变化的字节位于稳定指令签名中。
181-
let image = [0x85, 0xC0, 0x75, 0x07, 0xBE, 0x00, 0x12, 0x80];
182-
let pattern = [0x85, 0xC0, 0x75, 0x07, 0xBE, 0x00, 0x00, 0x80];
183-
184-
// When: TLS 前扫描器使用与晚期 patch 相同的掩码。
185-
let found = find_pattern(&image, &pattern, "xxxxxx?x");
186-
187-
// Then: 可变字节不会破坏版本无关识别。
188-
assert_eq!(found, Some(0));
189-
}
190-
191-
#[test]
192-
fn enabled_checks_are_patched_by_shared_early_pipeline() {
193-
// Given: AppUser 与 TLS 检测仍是原字节,且配置明确启用两项绕过。
194-
let mut image = vec![0x90; 64];
195-
image[8..14].copy_from_slice(&[0x83, 0x7C, 0x24, 0x04, 0x00, 0x75]);
196-
image[24..40].copy_from_slice(&[
197-
0x85, 0xC0, 0x75, 0x07, 0xBE, 0x00, 0x00, 0x80, 0x00, 0xEB, 0x02, 0x33, 0xF6, 0x8B,
198-
0x5B, 0x34,
199-
]);
200-
let memory = FakeMemory {
201-
base: 0x1000,
202-
image: RefCell::new(image),
203-
};
204-
let config = Config {
205-
base_dir: ".".to_owned(),
206-
sections: "[BypassAppUser]\n[DisableTLS]"
207-
.parse()
208-
.expect("测试配置必须有效"),
209-
};
210-
211-
// When: DLL_PROCESS_ATTACH 使用与晚期阶段共享的 patch 定义。
212-
patches::apply_early(&memory, &config);
213-
214-
// Then: 两个会在 TLS 初始化中被缓存的检测都已提前改写。
215-
let image = memory.image.borrow();
216-
assert_eq!(image[13], 0xEB);
217-
assert_eq!(image[31], 0x00);
218-
}
219-
}
165+
mod tests;

0 commit comments

Comments
 (0)