Skip to content

Commit 6fd9682

Browse files
committed
refactor: 去除硬编码地址,改用 AOB/IAT hook/字符串引用定位
- skip_startup/free_play/unlock_tracks/bypass/timers/audio 2ch: AOB pattern 定位 - unlock_tracks max: 搜 MOV EAX,3; RET 函数签名 - custom_freeplay: 搜 FREE PLAY 字符串 + PUSH 指令提取长度地址 - timers custom: 搜 PUSH 0x384/PUSH 0xA/PUSH 序列定位 3 处计时器 - network TLS: IAT hook WinHttpOpenRequest 清除 WINHTTP_FLAG_SECURE - network enc: 搜 cannot encrypt. 字符串交叉引用定位加密初始化函数 - audio shared: 搜 MOV [ESI+0xa0],1 指令改 EXCLUSIVE→SHARED 仍为硬编码(版本更新需手动补): - skip_map_anim: .text 段内嵌数据标志,无函数/无 xref/无字符串,来自 evilleaker patch 表
1 parent c849ce3 commit 6fd9682

10 files changed

Lines changed: 253 additions & 101 deletions

File tree

src/patches/audio.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,7 @@ use crate::patch_engine::{apply_patch, PatchDef};
33
use crate::util::api::Api;
44

55
pub fn apply(api: &Api, config: &Config) {
6-
apply_patch(
7-
api,
8-
config,
9-
&PatchDef {
10-
name: "强制共享音频",
11-
section: "ForceSharedAudio",
12-
pattern: None,
13-
pattern_offset: 0,
14-
known_offsets: &[0xE29393],
15-
expected: &[0x01],
16-
patch: &[0x00],
17-
},
18-
);
6+
apply_shared_audio(api, config);
197
apply_patch(
208
api,
219
config,
@@ -24,9 +12,33 @@ pub fn apply(api: &Api, config: &Config) {
2412
section: "Force2chAudio",
2513
pattern: Some("83 C4 04 85 C0 75 3F 68 ?? ?? ?? ?? E8 ?? ?? ?? ?? B8 02 00 00 00"),
2614
pattern_offset: 5,
27-
known_offsets: &[0xE2944B],
15+
known_offsets: &[],
2816
expected: &[0x75, 0x3F],
2917
patch: &[0x90, 0x90],
3018
},
3119
);
3220
}
21+
22+
fn apply_shared_audio(api: &Api, config: &Config) {
23+
if !config.is_enabled("ForceSharedAudio") {
24+
return;
25+
}
26+
27+
// MOV dword ptr [ESI+0xa0], 1 → C7 86 A0 00 00 00 01 00 00 00
28+
// 改 01→00 使 AUDCLNT_SHAREMODE_EXCLUSIVE→SHARED
29+
let site = api.aob_scan(
30+
api.text_base(),
31+
api.text_size(),
32+
&[0xC7, 0x86, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
33+
"xxxxxx?xxx",
34+
);
35+
if site != 0 {
36+
let zero = [0u8; 1];
37+
if api.mem_write(site + 6, &zero) {
38+
api.log_info("补丁已应用: 强制共享音频");
39+
return;
40+
}
41+
}
42+
43+
api.log_warn("强制共享音频: 未找到目标指令");
44+
}

src/patches/bypass.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn apply(api: &Api, config: &Config) {
2323
section: "BypassAppUser",
2424
pattern: Some("83 7C 24 04 00 75"),
2525
pattern_offset: 5,
26-
known_offsets: &[0x89075],
26+
known_offsets: &[],
2727
expected: &[0x75],
2828
patch: &[0xEB],
2929
},
@@ -39,7 +39,7 @@ pub fn apply_bypass_120hz(api: &Api, config: &Config) {
3939
section: "Bypass120hz",
4040
pattern: Some("85 C0 74 3F ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 81 BC 24 34 02 00 00 80 07 00 00"),
4141
pattern_offset: 0,
42-
known_offsets: &[0x15810E],
42+
known_offsets: &[],
4343
expected: &[0x85, 0xC0, 0x74, 0x3F],
4444
patch: &[0xEB, 0x30, 0xEB, 0x2E],
4545
},
@@ -57,7 +57,7 @@ fn apply_bypass_1080p(api: &Api, config: &Config) {
5757
"81 BC 24 34 02 00 00 80 07 00 00 75 1F 81 BC 24 38 02 00 00 38 04 00 00 75 12",
5858
),
5959
pattern_offset: 0,
60-
known_offsets: &[0x15811C],
60+
known_offsets: &[],
6161
expected: BYPASS_1080P_EXPECTED,
6262
patch: BYPASS_1080P_PATCH,
6363
},

src/patches/custom_freeplay.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::config::Config;
22
use crate::util::api::Api;
3-
use crate::util::memory::{file_offset_to_va, patch_bytes, write_value, PatchResult};
3+
use crate::util::memory::{patch_bytes, write_value, PatchResult};
4+
use crate::util::pattern;
45

5-
const FREE_PLAY_TEXT_OFFSET: u32 = 0x14AD9BC;
66
const FREE_PLAY_TEXT_EXPECTED: &[u8] = b"FREE PLAY";
77
const MAX_CUSTOM_TEXT_LEN: usize = u8::MAX as usize;
88

@@ -22,15 +22,15 @@ pub fn apply(api: &Api, config: &Config) {
2222
return;
2323
}
2424

25-
let length_addr = file_offset_to_va(api, 0x3DF4E9);
26-
if length_addr == 0 || !write_value(api, length_addr, text_bytes.len() as u8) {
27-
api.log_warn("补丁写入失败: 自定义 FREE PLAY 文本长度");
25+
let text_addr = pattern::scan_bytes(api, b"FREE PLAY\0");
26+
if text_addr == 0 {
27+
api.log_warn("自定义 FREE PLAY 文本: 未找到字符串");
2828
return;
2929
}
3030

31-
let text_addr = file_offset_to_va(api, FREE_PLAY_TEXT_OFFSET);
32-
if text_addr == 0 {
33-
api.log_warn("自定义 FREE PLAY 文本地址转换失败");
31+
let length_addr = find_length_addr(api, text_addr);
32+
if length_addr == 0 || !write_value(api, length_addr, text_bytes.len() as u8) {
33+
api.log_warn("补丁写入失败: 自定义 FREE PLAY 文本长度");
3434
return;
3535
}
3636

@@ -41,6 +41,23 @@ pub fn apply(api: &Api, config: &Config) {
4141
}
4242
}
4343

44+
fn find_length_addr(api: &Api, text_addr: usize) -> usize {
45+
// PUSH len / PUSH text_addr → 6A xx 68 [addr32_LE]
46+
let addr_bytes = (text_addr as u32).to_le_bytes();
47+
let mut sig = [0u8; 7];
48+
sig[0] = 0x6A; // PUSH imm8
49+
// sig[1] = length (wildcard)
50+
sig[2] = 0x68; // PUSH imm32
51+
sig[3..7].copy_from_slice(&addr_bytes);
52+
let mask = "x?xxxxx";
53+
let found = api.aob_scan(api.text_base(), api.text_size(), &sig, mask);
54+
if found == 0 {
55+
return 0;
56+
}
57+
// length operand is at found + 1
58+
found + 1
59+
}
60+
4461
fn patch_free_play_text(api: &Api, addr: usize, text: &[u8]) -> PatchResult {
4562
if text.len() == FREE_PLAY_TEXT_EXPECTED.len() {
4663
return patch_bytes(api, addr, FREE_PLAY_TEXT_EXPECTED, text);

src/patches/free_play.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn apply(api: &Api, config: &Config) {
1111
section: "FreePlay",
1212
pattern: Some("E8 ?? ?? ?? ?? 3C 01 75 ?? 6A 09"),
1313
pattern_offset: 5,
14-
known_offsets: &[0x3DF4E4],
14+
known_offsets: &[],
1515
expected: &[0x3C, 0x01],
1616
patch: &[0x38, 0xC0],
1717
},

src/patches/network.rs

Lines changed: 151 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,156 @@
11
use crate::config::Config;
2-
use crate::patch_engine::{apply_patch, PatchDef};
32
use crate::util::api::Api;
3+
use crate::util::iat_hook::hook_iat;
4+
use crate::util::pattern;
5+
use std::ffi::c_void;
6+
use std::sync::atomic::{AtomicUsize, Ordering};
7+
8+
const WINHTTP_DLL: &str = "winhttp.dll";
9+
const WINHTTP_OPEN_REQUEST: &str = "WinHttpOpenRequest";
10+
// WINHTTP_FLAG_SECURE = 0x00800000
11+
const WINHTTP_FLAG_SECURE: u32 = 0x0080_0000;
12+
13+
type WinHttpOpenRequestFn = unsafe extern "system" fn(
14+
*mut c_void, *const u16, *const u16, *const u16,
15+
*const u16, *const *const u16, u32,
16+
) -> *mut c_void;
17+
18+
static ORIG_OPEN_REQUEST: AtomicUsize = AtomicUsize::new(0);
419

520
pub fn apply(api: &Api, config: &Config) {
6-
apply_patch(
7-
api,
8-
config,
9-
&PatchDef {
10-
name: "关闭网络加密 1",
11-
section: "DisableEncryption",
12-
pattern: None,
13-
pattern_offset: 0,
14-
known_offsets: &[0x17D200C],
15-
expected: &[0xF5],
16-
patch: &[0x00],
17-
},
18-
);
19-
apply_patch(
20-
api,
21-
config,
22-
&PatchDef {
23-
name: "关闭网络加密 2",
24-
section: "DisableEncryption",
25-
pattern: None,
26-
pattern_offset: 0,
27-
known_offsets: &[0x17D2010],
28-
expected: &[0xF5],
29-
patch: &[0x00],
30-
},
31-
);
32-
apply_patch(
33-
api,
34-
config,
35-
&PatchDef {
36-
name: "关闭 TLS",
37-
section: "DisableTLS",
38-
pattern: None,
39-
pattern_offset: 0,
40-
known_offsets: &[0xE0D3FB],
41-
expected: &[0x80],
42-
patch: &[0x00],
43-
},
44-
);
21+
apply_disable_encryption(api, config);
22+
apply_disable_tls(api, config);
23+
}
24+
25+
fn apply_disable_encryption(api: &Api, config: &Config) {
26+
if !config.is_enabled("DisableEncryption") {
27+
return;
28+
}
29+
30+
let found = pattern::scan_bytes(api, b"cannot encrypt.\0");
31+
if found == 0 {
32+
api.log_warn("关闭网络加密: 未找到加密标识字符串");
33+
return;
34+
}
35+
36+
let addr_bytes = (found as u32).to_le_bytes();
37+
// 68 [addr] = PUSH <string_addr>
38+
let mut push_sig = [0u8; 5];
39+
push_sig[0] = 0x68;
40+
push_sig[1..5].copy_from_slice(&addr_bytes);
41+
42+
let text_base = api.text_base();
43+
let text_size = api.text_size();
44+
let mut search_start = text_base;
45+
let mut patched = 0u32;
46+
47+
loop {
48+
let remaining = text_size.saturating_sub((search_start - text_base) as u32);
49+
if remaining < 5 {
50+
break;
51+
}
52+
53+
let push_site = api.aob_scan(search_start, remaining, &push_sig, "xxxxx");
54+
if push_site == 0 {
55+
break;
56+
}
57+
58+
if let Some(func_start) = find_function_start(api, push_site, text_base) {
59+
if patch_encrypt_flag_in_function(api, func_start, push_site) {
60+
patched += 1;
61+
}
62+
}
63+
64+
search_start = push_site + 5;
65+
}
66+
67+
if patched > 0 {
68+
api.log_info(&format!("补丁已应用: 关闭网络加密 ({patched} 处)"));
69+
} else {
70+
api.log_warn("关闭网络加密: 未找到加密标志");
71+
}
72+
}
73+
74+
fn find_function_start(api: &Api, addr: usize, text_base: usize) -> Option<usize> {
75+
// 55 8B EC 6A FF = PUSH EBP / MOV EBP,ESP / PUSH -1
76+
let prologue = [0x55, 0x8B, 0xEC, 0x6A, 0xFF];
77+
for back in 1..0x800usize {
78+
let candidate = addr.checked_sub(back)?;
79+
if candidate < text_base {
80+
return None;
81+
}
82+
let mut buf = [0u8; 5];
83+
if api.mem_read(candidate, &mut buf) && buf == prologue {
84+
return Some(candidate);
85+
}
86+
}
87+
None
88+
}
89+
90+
fn patch_encrypt_flag_in_function(api: &Api, func_start: usize, ref_site: usize) -> bool {
91+
let func_end = ref_site + 0x200;
92+
// MOV dword ptr [param_1+4], imm32 → C7 41 04 xx xx xx xx
93+
let mut scan_addr = func_start;
94+
while scan_addr < func_end {
95+
let remaining = (func_end - scan_addr) as u32;
96+
if remaining < 7 {
97+
break;
98+
}
99+
let site = api.aob_scan(scan_addr, remaining, &[0xC7, 0x41, 0x04], "xxx");
100+
if site == 0 {
101+
break;
102+
}
103+
let mut val_buf = [0u8; 4];
104+
if api.mem_read(site + 3, &mut val_buf) {
105+
let val = u32::from_le_bytes(val_buf);
106+
if val != 0 && val < 0x1000 {
107+
let zero = [0u8; 4];
108+
if api.mem_write(site + 3, &zero) {
109+
return true;
110+
}
111+
}
112+
}
113+
scan_addr = site + 7;
114+
}
115+
false
116+
}
117+
118+
fn apply_disable_tls(api: &Api, config: &Config) {
119+
if !config.is_enabled("DisableTLS") {
120+
return;
121+
}
122+
123+
let original = unsafe {
124+
hook_iat(
125+
api.game_base(),
126+
WINHTTP_DLL,
127+
WINHTTP_OPEN_REQUEST,
128+
hooked_open_request as *const (),
129+
)
130+
};
131+
132+
if let Some(orig) = original {
133+
ORIG_OPEN_REQUEST.store(orig as usize, Ordering::SeqCst);
134+
api.log_info("补丁已应用: 关闭 TLS (WinHttpOpenRequest IAT hook)");
135+
} else {
136+
api.log_warn("关闭 TLS: 未找到 WinHttpOpenRequest 导入");
137+
}
138+
}
139+
140+
unsafe extern "system" fn hooked_open_request(
141+
h_connect: *mut c_void,
142+
verb: *const u16,
143+
object_name: *const u16,
144+
version: *const u16,
145+
referrer: *const u16,
146+
accept_types: *const *const u16,
147+
flags: u32,
148+
) -> *mut c_void {
149+
let orig_addr = ORIG_OPEN_REQUEST.load(Ordering::SeqCst);
150+
if orig_addr == 0 {
151+
return std::ptr::null_mut();
152+
}
153+
154+
let orig: WinHttpOpenRequestFn = std::mem::transmute(orig_addr);
155+
orig(h_connect, verb, object_name, version, referrer, accept_types, flags & !WINHTTP_FLAG_SECURE)
45156
}

src/patches/skip_startup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn apply(api: &Api, config: &Config) {
1111
section: "SkipStartup",
1212
pattern: Some("6A 07 8B CF E8 ?? ?? ?? ?? 6A 01 E8 ?? ?? ?? ?? 8B 35"),
1313
pattern_offset: 9,
14-
known_offsets: &[0x99B21A],
14+
known_offsets: &[],
1515
expected: &[0x6A, 0x01],
1616
patch: &[0x6A, 0x04],
1717
},

0 commit comments

Comments
 (0)