-->
当前位置:首页 > DayDayUp > 正文内容

js debugger各种方法的演示

Luz5天前DayDayUp80

做了个js debugger各种方法的演示,供绕过debugger学习用

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Debugger 策略与绕过演练台</title>
<style>
  :root {
    --primary:#2563eb;
    --danger:#ef4444;
    --bg:#0b1220;
    --card:#0f172a;
    --muted:#94a3b8;
    --text:#e2e8f0;
    --accent:#22c55e;
  }
  *{box-sizing:border-box}
  body{
    margin:0;
    font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
    background: radial-gradient(1200px 600px at 10% -10%, #172554 0%, #0b1220 45%) , var(--bg);
    color:var(--text);
    line-height:1.6;
  }
  header{
    max-width:1080px;margin:32px auto 8px;padding:0 16px;text-align:center;
  }
  h1{margin:0 0 8px 0;font-weight:800;letter-spacing:.2px}
  .sub{color:var(--muted);max-width:800px;margin:0 auto 20px}
  .wrap{max-width:1080px;margin:0 auto;padding:0 16px 32px}
  .card{
    background:linear-gradient(180deg, rgba(148,163,184,.1), transparent 30%) , var(--card);
    border:1px solid rgba(148,163,184,.15);
    border-radius:16px;
    padding:18px;
    margin-bottom:16px;
    box-shadow:0 10px 30px rgba(2,6,23,.4);
  }
  .grid{
    display:grid;
    grid-template-columns: repeat(2, minmax(0,1fr));
    gap:12px;
  }
  @media (min-width:860px){
    .grid{grid-template-columns: repeat(3, minmax(0,1fr));}
  }
  .row{
    display:flex;gap:8px;align-items:center;justify-content:flex-start;flex-wrap:wrap;
    padding:10px;border:1px dashed rgba(148,163,184,.2);border-radius:12px;background:rgba(30,41,59,.35)
  }
  .row h3{
    margin:0 8px 0 0;font-size:15px;font-weight:700;white-space:nowrap
  }
  button{
    padding:9px 12px;font-size:14px;border:none;border-radius:10px;color:white;cursor:pointer;
    background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(0,0,0,.08)), var(--primary);
    transition:filter .2s, transform .02s;
  }
  button.small{padding:8px 10px;font-size:13px;background:#334155}
  button:hover{filter:brightness(1.08)}
  button:active{transform:translateY(1px)}
  button.red{background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(0,0,0,.08)), var(--danger)}
  .log{
    height:200px;overflow:auto;background:rgba(15,23,42,.7);border-radius:12px;border:1px solid rgba(148,163,184,.15);
    padding:10px;font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;font-size:13px;
  }
  .log p{margin:4px 0}
  .hint{color:var(--muted);font-size:13px;margin:-6px 0 8px}
  /* Modal */
  .modal-backdrop{
    position:fixed;inset:0;background:rgba(2,6,23,.6);backdrop-filter: blur(3px);
    display:none;align-items:center;justify-content:center;padding:16px;z-index:40;
  }
  .modal{
    max-width:820px;width:100%;
    background:var(--card);border:1px solid rgba(148,163,184,.2);border-radius:14px;
    box-shadow:0 20px 60px rgba(2,6,23,.6);
    overflow:hidden;
  }
  .modal header{
    padding:14px 16px 0;margin:0;text-align:left;
  }
  .modal h2{margin:0 0 4px 0;font-size:18px}
  .modal .content{
    padding:4px 16px 16px;max-height:60vh;overflow:auto;color:#dbeafe;
  }
  .modal .content ul{padding-left:18px;margin:6px 0 0 0}
  .modal .content li{margin:4px 0}
  .modal .footer{
    padding:12px 16px;border-top:1px solid rgba(148,163,184,.2);display:flex;justify-content:flex-end;gap:8px
  }
  .badge{
    display:inline-block;padding:2px 8px;border-radius:999px;font-size:12px;color:white;background:#0ea5e9;margin-left:6px
  }
</style>
</head>
<body>
  <header>
    <h1>Debugger 策略与绕过演练台</h1>
    <p class="sub">点击任意触发按钮开启对应的 <code>debugger</code> 策略;旁边的“绕过提示”会给出常见规避方案与工具路径。适合在 DevTools 中练习反调试绕过。</p>
  </header>

  <div class="wrap">
    <section class="card">
      <div class="hint">▶ 建议开启 DevTools 的 <em>Pause on debugger</em>,并试试条件断点、覆盖脚本、CSP 调整等。</div>
      <div class="grid" id="btnGrid">
        <!-- 按钮行通过 JS 注入,见下方 config -->
      </div>
      <div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">
        <button id="stopAllBtn" class="red">停止所有触发</button>
        <button id="clearLogBtn" class="small">清空日志</button>
      </div>
    </section>

    <section class="card">
      <h3 style="margin:0 0 8px 0">日志</h3>
      <div class="log" id="logContainer"><p>日志输出区域...</p></div>
    </section>
  </div>

  <!-- Modal -->
  <div class="modal-backdrop" id="modal">
    <div class="modal">
      <header>
        <h2 id="modalTitle">绕过提示</h2>
      </header>
      <div class="content" id="modalContent">
        <!-- 动态填充 -->
      </div>
      <div class="footer">
        <button id="copyTipsBtn" class="small">复制内容</button>
        <button id="closeModalBtn">关闭</button>
      </div>
    </div>
  </div>

<script>
/* ========= 日志 / 公共 ========= */
function log(message){
  const box = document.getElementById('logContainer');
  const p = document.createElement('p');
  p.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
  box.appendChild(p);
  box.scrollTop = box.scrollHeight;
}
window.log = log; // 供 new Function / Worker 使用

// 可清理资源的引用
const timers = {
  explicit:null, eval:null, fn:null, ctor:null, mixed:null,
  raf:null, timeout:null, promise:false, observer:null, getterInt:null, idle:null, worker:null,
  evThrottler:{ last:0, handler:null }
};

function jitter(base=1200, spread=600){
  return base - spread/2 + Math.random()*spread;
}

/* ========= 触发策略配置(名称、触发器、绕过指南) ========= */
const strategies = [
  {
    key:'explicit',
    name:'显式 setInterval',
    trigger(){
      if (timers.explicit) return log('显式已在运行');
      log('启动:显式 setInterval');
      function tick(){ debugger; log('显式 debugger 触发'); }
      timers.explicit = setInterval(tick, 1000);
    },
    tips:`
<ul>
  <li>在 DevTools 勾选「从不在异常处暂停」/关闭 Pause on debugger。</li>
  <li>临时把 <code>debugger</code> 改成空语句:在 Sources 里覆盖脚本(Override/Local Overrides)。</li>
  <li>在运行时重写 <code>window.Function.prototype.constructor</code>/<code>debugger</code> 触发点(若可控)。</li>
  <li>全局禁用定时器:把 <code>setInterval</code> 指向空函数(慎用,影响面大)。</li>
</ul>`
  },
  {
    key:'eval',
    name:'eval 调用',
    trigger(){
      if (timers.eval) return log('eval 已在运行');
      log('启动:eval 调用');
      timers.eval = setInterval(()=>{
        try { eval("debugger; window.log('eval debugger 触发')"); }
        catch(e){ debugger; log('eval 被 CSP 限制,降级显式触发'); }
      }, 1000);
    },
    tips:`
<ul>
  <li>站点有 CSP:在 Response Header 放宽 <code>script-src 'unsafe-eval'</code> 或调整 DevTools 的 Overrides 以去除限制(仅在可控环境调试)。</li>
  <li>直接将 <code>eval</code> 指向安全实现:<code>window.eval = ()=>{}</code>(可能影响站点其他逻辑)。</li>
  <li>改为条件断点:只在特定堆栈/URL 时暂停。</li>
</ul>`
  },
  {
    key:'fn',
    name:'new Function',
    trigger(){
      if (timers.fn) return log('Function 已在运行');
      log('启动:new Function');
      timers.fn = setInterval(()=>{
        try { new Function("debugger; window.log('Function debugger 触发')")(); }
        catch(e){ debugger; log('new Function 被 CSP 限制,降级显式触发'); }
      }, 1000);
    },
    tips:`
<ul>
  <li>CSP 下常被拦截:同 <code>eval</code> 绕过。</li>
  <li>在模块脚本中,它无法捕获局部变量;将依赖挂到 <code>window</code>。</li>
  <li>可用 DevTools「黑盒」脚本避免进入该文件。</li>
</ul>`
  },
  {
    key:'ctor',
    name:'Function.prototype.constructor',
    trigger(){
      if (timers.ctor) return log('Constructor 已在运行');
      log('启动:Function.prototype.constructor');
      timers.ctor = setInterval(()=>{
        try { (function(){}).constructor("debugger; window.log('Constructor debugger 触发')")(); }
        catch(e){ debugger; log('constructor 被 CSP 限制,降级显式触发'); }
      }, 1000);
    },
    tips:`
<ul>
  <li>与 <code>new Function</code> 等价,亦受 CSP 约束。</li>
  <li>可以在运行时替换 <code>Function.prototype.constructor</code> 返回的实现。</li>
</ul>`
  },
  {
    key:'mixed',
    name:'混合随机',
    trigger(){
      if (timers.mixed) return log('混合已在运行');
      log('启动:混合随机(含降级、抖动)');
      function once(){
        const arr = [
          ()=>{ debugger; log('显式 触发'); },
          ()=>{ try{ eval("debugger; window.log('eval 触发')"); }catch{ debugger; log('eval 降级'); } },
          ()=>{ try{ new Function("debugger; window.log('Function 触发')")(); }catch{ debugger; log('Function 降级'); } },
          ()=>{ try{ (function(){}).constructor("debugger; window.log('Ctor 触发')")(); }catch{ debugger; log('Ctor 降级'); } }
        ];
        arr[Math.floor(Math.random()*arr.length)]();
      }
      (function loop(){
        once();
        timers.mixed = setTimeout(loop, jitter());
      })();
    },
    tips:`
<ul>
  <li>随机调度+多路径:用条件断点限制只在感兴趣的路径暂停。</li>
  <li>在 Overrides 中批量删改所有 <code>debugger;</code>(正则查找)。</li>
</ul>`
  },
  {
    key:'raf',
    name:'requestAnimationFrame',
    trigger(){
      if (timers.raf) return log('rAF 已在运行');
      log('启动:requestAnimationFrame 循环');
      const loop=()=>{ debugger; log('rAF 触发'); timers.raf = requestAnimationFrame(loop); };
      timers.raf = requestAnimationFrame(loop);
    },
    tips:`
<ul>
  <li>后台标签页 rAF 会暂停:切后台临时规避。</li>
  <li>DevTools「黑盒脚本」+ 移除 <code>debugger</code>。</li>
</ul>`
  },
  {
    key:'timeout',
    name:'setTimeout 递归(抖动)',
    trigger(){
      if (timers.timeout) return log('Timeout 已在运行');
      log('启动:setTimeout 递归(抖动)');
      const run = ()=>{ debugger; log('Timeout 触发'); timers.timeout = setTimeout(run, jitter()); };
      timers.timeout = setTimeout(run, 600);
    },
    tips:`
<ul>
  <li>重写 <code>setTimeout</code>,拦截特定回调。</li>
  <li>条件断点过滤调用栈/脚本 URL。</li>
</ul>`
  },
  {
    key:'promise',
    name:'Promise/Microtask',
    trigger(){
      if (timers.promise) return log('Promise 已在运行');
      log('启动:Promise/Microtask');
      timers.promise = true;
      (function schedule(){
        if (!timers.promise) return;
        Promise.resolve().then(()=>{ debugger; log('Promise 微任务触发'); })
          .finally(()=> setTimeout(schedule, 800));
      })();
    },
    tips:`
<ul>
  <li>微任务频繁:在 DevTools 里使用「Async 堆栈」辅助溯源。</li>
  <li>禁用/替换 <code>Promise</code> 的 then 包装(风险大,不推荐生产)。</li>
</ul>`
  },
  {
    key:'mo',
    name:'MutationObserver',
    trigger(){
      if (timers.observer) return log('MO 已在运行');
      log('启动:MutationObserver');
      const target = document.getElementById('logContainer');
      const mo = new MutationObserver(()=>{ debugger; log('MO 触发'); });
      mo.observe(target, {childList:true, characterData:true, subtree:true});
      timers.observer = mo;
      let i=0;
      (function poke(){
        if (!timers.observer) return;
        const p=document.createElement('p'); p.textContent='MO 心跳 '+(++i); target.appendChild(p);
        setTimeout(poke, 1500);
      })();
    },
    tips:`
<ul>
  <li>临时断开观察者:覆盖 <code>MutationObserver.prototype.observe</code>/<code>disconnect</code>。</li>
  <li>避免 DOM 变更(若可控数据源)。</li>
</ul>`
  },
  {
    key:'getter',
    name:'Getter/Proxy 访问',
    trigger(){
      if (timers.getterInt) return log('Getter 已在运行');
      log('启动:Getter/Proxy');
      const carrier = {};
      Object.defineProperty(carrier,'x',{get(){ debugger; log('Getter 触发'); return 1; }});
      timers.getterInt = setInterval(()=>{ void carrier.x; }, 1000);
    },
    tips:`
<ul>
  <li>用 <code>Object.defineProperty</code> 重新定义该属性,无副作用 getter。</li>
  <li>若为 Proxy,可替换 <code>Proxy</code> 构造/handler。</li>
</ul>`
  },
  {
    key:'idle',
    name:'requestIdleCallback',
    trigger(){
      if (timers.idle) return log('Idle 已在运行');
      if (!('requestIdleCallback' in window)){
        log('不支持 requestIdleCallback,降级 setTimeout');
        timers.idle = setTimeout(function tick(){ debugger; log('Idle 降级触发'); timers.idle=setTimeout(tick,1200); }, 800);
        return;
      }
      log('启动:requestIdleCallback');
      const loop=()=>{ timers.idle = requestIdleCallback(()=>{ debugger; log('Idle 触发'); loop(); }, {timeout:2000}); };
      loop();
    },
    tips:`
<ul>
  <li>空闲触发:让页面保持忙碌(动画/网络)以推迟触发。</li>
  <li>替换 <code>requestIdleCallback</code> 为 no-op(仅测试用)。</li>
</ul>`
  },
  {
    key:'worker',
    name:'Web Worker',
    trigger(){
      if (timers.worker) return log('Worker 已在运行');
      log('启动:Web Worker(需 DevTools 开启 Worker 源码调试)');
      try{
        const blob=new Blob([`
          self.onmessage=()=>{};
          setInterval(()=>{ debugger; self.postMessage('tick'); }, 1000);
        `],{type:'application/javascript'});
        const url=URL.createObjectURL(blob);
        const w=new Worker(url);
        w.onmessage = e => { /* 占位 */ };
        timers.worker=w;
      }catch(e){
        log('创建 Worker 失败(可能被 CSP 的 worker-src / blob 禁止):'+e);
      }
    },
    tips:`
<ul>
  <li>Sources 面板展开「Workers」,单独禁用暂停或黑盒 worker 脚本。</li>
  <li>CSP 严格下可禁用 blob/worker-src,阻止加载(仅自测环境)。</li>
</ul>`
  },
  {
    key:'event',
    name:'事件监听(mousemove/keydown)',
    trigger(){
      if (timers.evThrottler.handler) return log('事件监听已在运行');
      log('启动:事件监听(1s 节流)');
      const throttled = (e)=>{
        const now=Date.now(); if (now - timers.evThrottler.last < 1000) return;
        timers.evThrottler.last = now; debugger; log(`事件 ${e.type} 触发`);
      };
      timers.evThrottler.handler = throttled;
      window.addEventListener('mousemove', throttled, {passive:true});
      window.addEventListener('keydown', throttled, {passive:true});
    },
    tips:`
<ul>
  <li>避免触发相关事件(或在控制台移除监听)。</li>
  <li>运行时用 <code>getEventListeners(window)</code>(Chrome)查找并 <code>removeEventListener</code>。</li>
</ul>`
  }
];

/* ========= 注入按钮行 ========= */
const grid = document.getElementById('btnGrid');
strategies.forEach(s=>{
  const row = document.createElement('div');
  row.className = 'row';
  row.innerHTML = `
    <h3>${s.name}</h3>
    <button data-key="${s.key}">触发</button>
    <button class="small" data-tip="${s.key}">绕过提示</button>
    <span class="badge">ID: ${s.key}</span>
  `;
  grid.appendChild(row);
});
grid.addEventListener('click',(e)=>{
  const triggerKey = e.target.getAttribute('data-key');
  const tipKey = e.target.getAttribute('data-tip');
  if (triggerKey){
    const s = strategies.find(x=>x.key===triggerKey);
    if (s) s.trigger();
  } else if (tipKey){
    const s = strategies.find(x=>x.key===tipKey);
    if (s) openTips(s.name, s.tips);
  }
});

/* ========= 停止/清理 ========= */
document.getElementById('stopAllBtn').addEventListener('click', ()=>{
  // interval 类
  ['explicit','eval','fn','ctor','getterInt'].forEach(k=>{
    if (timers[k]){ clearInterval(timers[k]); timers[k]=null; }
  });
  // timeout / mixed
  if (timers.timeout){ clearTimeout(timers.timeout); timers.timeout=null; }
  if (timers.mixed){ clearTimeout(timers.mixed); timers.mixed=null; }
  // rAF
  if (timers.raf){ cancelAnimationFrame(timers.raf); timers.raf=null; }
  // Promise 循环标志
  timers.promise = false;
  // MO
  if (timers.observer){ timers.observer.disconnect(); timers.observer=null; }
  // idle
  if (timers.idle){
    if ('cancelIdleCallback' in window) cancelIdleCallback(timers.idle);
    else clearTimeout(timers.idle);
    timers.idle=null;
  }
  // Worker
  if (timers.worker){ timers.worker.terminate(); timers.worker=null; }
  // 事件监听
  if (timers.evThrottler.handler){
    window.removeEventListener('mousemove', timers.evThrottler.handler);
    window.removeEventListener('keydown',  timers.evThrottler.handler);
    timers.evThrottler.handler=null; timers.evThrottler.last=0;
  }
  log('已停止所有触发');
});
document.getElementById('clearLogBtn').addEventListener('click', ()=>{
  const box=document.getElementById('logContainer');
  box.innerHTML = '<p>日志输出区域...</p>';
});

/* ========= Modal(绕过提示) ========= */
const modal = document.getElementById('modal');
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
const closeModalBtn = document.getElementById('closeModalBtn');
const copyTipsBtn  = document.getElementById('copyTipsBtn');

function openTips(title, html){
  modalTitle.textContent = `绕过提示 · ${title}`;
  modalContent.innerHTML = html + `
    <hr style="border-color:rgba(148,163,184,.2)">
    <p style="margin:6px 0 0 0;color:#93c5fd;font-size:13px">
    通用思路:条件断点(过滤堆栈/URL/调用参数) · 覆盖脚本移除 <code>debugger</code> · 黑盒脚本 · 临时替换 API(<code>eval</code>/<code>Function</code>/<code>setInterval</code>/Observers 等) · 关闭 Pause on debugger。
    </p>`;
  modal.style.display = 'flex';
}
function closeTips(){ modal.style.display = 'none'; }

closeModalBtn.addEventListener('click', closeTips);
modal.addEventListener('click',(e)=>{ if (e.target===modal) closeTips(); });
copyTipsBtn.addEventListener('click', async ()=>{
  const tmp = modalContent.innerText;
  try{
    await navigator.clipboard.writeText(tmp);
    log('已复制绕过提示到剪贴板');
  }catch{
    log('复制失败:可能缺少权限');
  }
});

</script>
</body>
</html>
返回列表

上一篇:E01镜像仿真(E01镜像转VMware虚拟机)

没有最新的文章了...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。