use std::time::Duration;
use drission::prelude::*;
#[tokio::main]
async fn main() -> drission::Result<()> {
let headless = std::env::var("HL").map(|v| v != "0").unwrap_or(true);
let url = std::env::var("URL").unwrap_or_else(|_| "https://mh.yichengwlkj.com/pc".to_string());
let secs: u64 = std::env::var("SEC")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(8);
let window = Duration::from_secs(secs);
println!("[*] 目标: {url}(headless={headless},窗口 {secs}s)");
let browser = ChromiumBrowser::launch(ChromiumOptions::new().headless(headless)).await?;
let none = GetOptions::new().load_mode(LoadMode::None);
println!("\n========== Phase A:不 defuse(基线)==========");
let tab_a = browser.new_tab(Some("about:blank")).await?;
let dbg_a = tab_a.debugger();
dbg_a.enable().await?;
tab_a.get_with(&url, &none).await?;
let (n_a, bt_a) = dbg_a.count_pauses(window, true).await?;
println!("[A] {secs}s 内 Debugger.paused 触发 {n_a} 次(数字越高=反调试越凶)");
if !bt_a.is_empty() {
println!("[A] 首个暂停的调用栈(直指反调试代码):");
for l in bt_a.lines().take(6) {
println!(" {l}");
}
}
tab_a.close().await.ok();
println!("\n========== Phase B:tab.anti_anti_debug() 注入式 defuse ==========");
let tab_b = browser.new_tab(Some("about:blank")).await?;
tab_b.anti_anti_debug().await?; let dbg_b = tab_b.debugger();
dbg_b.enable().await?;
tab_b.get_with(&url, &none).await?;
let (n_b, _bt_b) = dbg_b.count_pauses(window, true).await?;
println!("[B] {secs}s 内 Debugger.paused 触发 {n_b} 次(注入式:拦直接/eval 生成的 debugger)");
println!(
"\n========== Phase C:tab.debugger().set_skip_all_pauses(true)(CDP 原生通杀)=========="
);
let tab_c = browser.new_tab(Some("about:blank")).await?;
let dbg_c = tab_c.debugger();
dbg_c.enable().await?;
dbg_c.set_skip_all_pauses(true).await?; tab_c.get_with(&url, &none).await?;
dbg_c.set_skip_all_pauses(true).await?; let (n_c, _bt_c) = dbg_c.count_pauses(window, true).await?; println!("[C] {secs}s 内 Debugger.paused 触发 {n_c} 次(应 ~0)");
let title = tab_c.title().await.unwrap_or_default();
let href = tab_c.run_js("location.href").await.ok();
let webdriver = tab_c.run_js("navigator.webdriver").await.ok();
println!("[C] title={title:?} location={href:?} navigator.webdriver={webdriver:?}");
println!("\n========== ② 脚本工具(先导航再 list,list 内部自动 skip 防卡)==========");
let tab_s = browser.new_tab(Some("about:blank")).await?;
tab_s.get_with(&url, &none).await?;
tokio::time::sleep(Duration::from_secs(5)).await; let sc = tab_s.scripts();
let scripts = sc.list().await.unwrap_or_default();
println!("[S] 解析到 {} 个脚本", scripts.len());
for kw in [
"x-ca-sign",
"checkPerformance",
"debugger",
"HmacSHA256",
"newSign",
] {
let hits = sc.grep(kw).await.unwrap_or_default();
println!("[S] grep {kw:?} → {} 处命中", hits.len());
for m in hits.iter().take(2) {
let frag: String = m.snippet.chars().take(80).collect();
println!(" {} :{} {frag}", short(&m.url), m.line_number);
}
}
println!("\n========== 结论 ==========");
println!("[*] Phase A 基线暂停 {n_a} 次;B(注入式){n_b} 次;C(CDP 通杀){n_c} 次。");
if n_a == 0 {
println!(
"ℹ️ 基线未触发(该站可能需交互触发反调试,或对无 DevTools 的 CDP 会话不启用);可 SEC=15 / HL=0 再试。"
);
} else {
if n_b.saturating_mul(2) < n_a {
println!("✅ 注入式 defuse 对该站直接/eval 型 debugger 有效(B 明显低于 A)。");
} else {
println!(
"➖ 注入式 defuse 对该站收效有限(debugger 藏在间接调用/已解析脚本里,注入改不到)。"
);
}
if n_c == 0 {
println!(
"✅ CDP 原生 set_skip_all_pauses 通杀:暂停 {n_a} → 0,页面照常加载(title={title:?}),可干净调试/dump。"
);
} else {
println!("⚠️ Phase C 仍有 {n_c} 次暂停(异常,预期 0)。");
}
}
browser.quit().await?;
Ok(())
}
fn short(u: &str) -> String {
u.rsplit('/').next().unwrap_or(u).chars().take(50).collect()
}