吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 846|回复: 5
上一主题 下一主题
收起左侧

[Web逆向] 某招聘网站的反调试与反反调试策略

  [复制链接]
跳转到指定楼层
楼主
桐姥爷 发表于 2026-3-9 02:49 回帖奖励
本帖最后由 桐姥爷 于 2026-3-9 02:51 编辑

某招聘网站的反调试分析与绕过总结

1. 网站反调试技术

分析文件:app.80fffb17.js

1.1 入口:abFn -> noDebug

// app.80fffb17.js:49815
(0, g.abFn)();
// app.80fffb17.js:52425-52431
e.abFn = function() {
  var t, e = null === (t = window.iGeekRoot) || void 0 === t || null === (t = t.aBService) || void 0 === t || null === (t = t.abData) || void 0 === t || null === (t = t.nd_result_13912_number_1) || void 0 === t ? void 0 : t.result;
  try {
    e !== "Q,M,&L,b".split(",").join("") && (0, r.noDebug)({
      env: "prod",
      appName: "zhipin_geek_spa_web"
    }, o.ndObj.cb)
  } catch (t) { console.log(t) }
}

含义:业务入口触发 abFn,再根据条件进入 noDebug

1.2 快捷键拦截(F12 / Ctrl+Shift+I/J / Ctrl+U/S)

// app.80fffb17.js:52696-52705
function rt(t) {
  const e = $[g(P)] ? (t, e) => t.metaKey && t.altKey && (73 === e || 74 === e) : (t, e) => t.ctrlKey && t.shiftKey && (73 === e || 74 === e),
        n = $[g(P)] ? (t, e) => t.metaKey && t.altKey && 85 === e || t.metaKey && 83 === e : (t, e) => t.ctrlKey && (83 === e || 85 === e);
  t.addEventListener(g("`;^<[S:5c45I"), r => {
    const o = (r = r || t.event).keyCode || r.which;
    if (123 === o || e(r, o) || n(r, o)) return function(t, e) {
      return (e = e || t.event).returnValue = !1, e.preventDefault(), !1
    }(t, r)
  }, !0)
}

含义:阻止常见打开开发者工具和查看源码的快捷键。

1.3 隐藏 iframe 取“干净原生 API”

// app.80fffb17.js:52670-52687
function J(t) {
  var e;
  try {
    W || (W = document.createElement("iframe"), W.style.display = "none", document.body.appendChild(W));
    if (Array.isArray(t)) return t.map(t => {
      var e;
      return null === (e = W.contentWindow) || void 0 === e ? void 0 : e[t]
    });
    return null === (e = W.contentWindow) || void 0 === e ? void 0 : e[t]
  } catch (e) {
    return Array.isArray(t) ? t.map(t => window[t]) : window[t]
  }
}

含义:通过 iframe.contentWindow 获取未被主窗口 hook 的原生方法,绕过简单补丁。

1.4 DevTools 检测轮询与触发链

// app.80fffb17.js:53067-53070
(() => {
  if (!t) {
    for (const t of Bt) t.type, t.XCID();
    et()
  }
}, 500)
// app.80fffb17.js:52973-52980
XCID() {
  var t, e;
  const n = g(a), r = g("[d^;oS:pcSVI"), o = g("`QW]`S:5"), i = g(s), u = g("wvWc0Mpcd^][Q{jX:rbu>;");
  (!0 === (null === (e = null === (t = window[n]) || void 0 === t ? void 0 : t[r]) || void 0 === e ? void 0 : e[o]) || window[i] && window.document.querySelector(u)) && this[g(c)]()
}
// app.80fffb17.js:52944-52946
}[g(c)]() {
  this.seact(), _t(xt), ht(), this.type
}

含义:每 500ms 执行检测器;命中 DevTools 条件后调用 onDevToolOpen,最终执行 ht()

1.5 触发后的破坏动作(核心)

// app.80fffb17.js:52873-52905
try {
  window[g("c:Jwc1l")] = null, window[g("c:Jwcu>;")](g(""), g("q:WwcSr;"))
} catch (t) { window.console.log(t) }
try {
  window[g("X;pbIn;")]()
} catch (t) { window.console.log(t) }
try {
  window[g("`dvswS:vX5I")][g("Xvdc`9>;")]()
} catch (t) { window.console.log(t) }
dt(() => {
  try { document[g(d)][g("`^R|[^ZeoaND")] = g("") } catch (t) { window.console.log(t) }
  try { window.location[g("`SBw[u>;")] = g("") } catch (t) { window.console.log(t) }
  !function() {
    const t = document.createElement("style"),
          e = [g("`Sr}cG>;"), g(d), g("XQJ~")],
          n = ["filter: blur(20px) !important", "display: none !important", "visibility: hidden !important", "opacity: 0 !important"],
          r = Math.floor(Math.random() * n.length);
    e.forEach(e => { t.textContent = `${e} { ${n[r]} }` }), document[g(ut)].appendChild(t)
  }()
}, 100)

含义:打开空页、关闭/后退、清空 body、改 location.href、注入隐藏样式,组合拳让页面不可用。

1.6 反篡改失败后的惩罚(内存炸弹)

// app.80fffb17.js:52817-52830
function lt(t) {
  let e = { success: !0, methods: {} };
  try {
    t.forEach(t => {
      let n = J(t);
      if (t.includes(".")) {
        const [e, r] = t.split(".");
        n = window[e][r]
      }
      n && /\[native code\]/.test(n.toString()) ? e.methods[t] = !0 : (e.success = !1, e.methods[t] = !1)
    })
  } catch (t) {}
  return e
}
// app.80fffb17.js:52707-52735
function ot() {
  try {
    const t = [], e = () => {
      for (let e = 0; e < 1e3; e++) {
        const n = {};
        for (let t = 0; t < 1e3; t++) n[`key_${e}_${t}`] = "x".repeat(1e3);
        t.push(n)
      }
      const e = [];
      for (let t = 0; t < 100; t++) e.push(new Array(1e4).fill("JBwd{b5S=[d^pg@M9`^rw0{3vd:cd{+bdWpX^g/[QBk1"));
      t.push(...e)
    };
    e();
    const r = window.setInterval(() => {
      try {
        const e = [];
        for (let t = 0; t < 1e3; t++) e.push(new Array(1e4).fill("x"));
        t.push(...e)
      } catch (t) { window.clearInterval(r) }
    }, 10)
  } catch (t) {}
}

含义:检测到“方法被改”后,触发内存/CPU 压制。

1.7 调用链

  1. abFn 执行(49815)  
  2. 条件触发 noDebug52428)  
  3. 检测轮询 for (const t of Bt) t.XCID()53069)  
  4. 检测命中 -> onDevToolOpen52944-52946)  
  5. onDevToolOpenht()52945)  
  6. ht() 执行跳转/清空/样式破坏(52873-52905)  

2. 通过 Hook 定位到关键代码

这一段是定位思路。

2.1 第一步:先抓“谁在跳转”

先在 document-start 注入最小 trap,只拦截并打印这些高价值 sink:

  • window.open
  • location.assign/replace/reload
  • location.href setter
  • history.back/go/forward

核心思路是:不先猜业务代码,而是先抓最终破坏动作调用者。

2.2 第二步:从运行时栈反推到 bundle 位置

最早抓到的关键日志形态是:

[REDIRECT-TRAP] open()
from: https://www.zhipin.com/web/geek/jobs
to:
stack:
  at ht (app.80fffb17.js:54:43498)
  at Et.onDevToolOpen (app.80fffb17.js:54:44611)
  at Et.XCID (app.80fffb17.js:54:48576)

这个栈直接给出了关键链路:

  • XCID(检测器)
  • onDevToolOpen(触发点)
  • ht(执行破坏动作)

于是再回到本地快照 app.80fffb17.js 按这些符号/模式定位,最终落到:

  • ht()app.80fffb17.js:52838 附近
  • onDevToolOpen[g(c)]()):app.80fffb17.js:52944-52946
  • XCID()app.80fffb17.js:5297353069 附近轮询调用

2.3 第三步:确认“它会绕过主窗口 hook”

在定位阶段发现 J(t) 会创建隐藏 iframe,并从 contentWindow 取原生方法:

// app.80fffb17.js:52670-52687
W || (W = document.createElement("iframe"), W.style.display = "none", document.body.appendChild(W));
return W.contentWindow[t];

这解释了为什么“只 hook 主窗口 API”一开始拦不全。

2.4 第四步:定位时使用的关键 Hook 代码(精简版)

// 关键:统一记录 from/to/stack
function trap(action, target) {
  const stack = new Error('[REDIRECT-TRAP] stack').stack;
  console.groupCollapsed(`[REDIRECT-TRAP] ${action}`);
  console.log('from:', location.href);
  console.log('to:', target);
  console.log(stack);
  console.groupEnd();
}

// 关键:先抓 window.open
const rawOpen = window.open;
window.open = function (...args) {
  trap('open()', args[0]);
  return rawOpen.apply(this, args);
};

// 关键:抓 location.href setter
const proto = Object.getPrototypeOf(window.location);
const hrefDesc = Object.getOwnPropertyDescriptor(proto, 'href');
Object.defineProperty(proto, 'href', {
  configurable: true,
  get: hrefDesc.get,
  set(url) {
    trap('location.href =', url);
    return hrefDesc.set.call(this, url);
  }
});

这套“先抓 sink -> 看 stack -> 回 bundle 对位”的方法,能在混淆代码里快速找到真正生效的反调试逻辑,而不是在大包里盲猜。


3. 绕过策略

实现文件:hook.js(Tampermonkey,@run-at document-start

3.1 总体策略

  • 不改站点 bundle,走运行时 hook。  
  • 主目标是阻断“破坏动作”(跳转、清空 DOM、样式遮蔽、惩罚定时器)。  
  • 避免误伤正常业务定时器,最后收敛为“高置信规则”。

3.2 跳转/刷新/窗口动作统一拦截

// hook.js:205-229(节选)
patchMethod(win.Location.prototype, 'assign', (args) => args[0], { lock: true, tag: prefix });
patchMethod(win.Location.prototype, 'replace', (args) => args[0], { lock: true, tag: prefix });
patchMethod(win.Location.prototype, 'reload', () => '[reload]', { lock: true, tag: prefix });

patchWindowLocationSetter(win, prefix); // window.location= / document.location=

patchMethod(win, 'open', (args) => args[0], { lock: true, tag: prefix });
patchMethod(win, 'stop', () => '[stop]', { lock: true, tag: prefix });
patchMethod(win, 'close', () => '[close]', { lock: true, tag: prefix });

含义:直接封堵 ht() 里的主要跳转/关闭路径。

3.3 DOM 破坏防护

// hook.js:295-311(节选)
if (this === document.body && stackMatched(stack)) {
  console.warn('[REDIRECT-TRAP] blocked body.innerHTML overwrite');
  return;
}
return innerHTMLDesc.set.call(this, value);
// hook.js:327-335(节选)
const suspiciousStyle = /blur\(20px\)|display:\s*none|visibility:\s*hidden|opacity:\s*0/i.test(css);
if (suspiciousStyle && stackMatched(stack)) {
  console.warn('[REDIRECT-TRAP] blocked suspicious STYLE append');
  return node;
}

含义:阻断“清空 body + 注入隐藏样式”。

3.4 解决“隐藏 iframe 绕过 hook”

// hook.js:255-260(节选)
function patchIframeNode(node, reason) {
  if (!node || node.tagName !== 'IFRAME') return;
  const install = () => {
    if (node.contentWindow) installWindowGuards(node.contentWindow, `iframe(${reason})`);
  };
  install();
}
// hook.js:336-354(节选)
const result = rawAppendChild.call(this, node);
patchIframeNode(node, 'appendChild');
...
const result = rawInsertBefore.call(this, newNode, referenceNode);
patchIframeNode(newNode, 'insertBefore');

含义:页面新增 iframe 时,把同样防护注入到 iframe.contentWindow,补上绕过口。

3.5 定时器策略:从“全拦截”收敛到“高置信拦截”

// hook.js:25-26
const STACK_FLAG = /onDevToolOpen|XCID|noDebug|(?:^|\W)abFn(?:\W|$)|(?:^|\W)ot(?:\W|$)/i;
const CALLBACK_FLAG = /new Array\(1e4\)\.fill\(['"]x['"]\)|['"]x['"]\.repeat\(1e4\)|nested_/i;
// hook.js:173-178
function shouldBlockTimer(callback) {
  if (!CONFIG.blockSuspiciousTimers) return false;
  const timerStack = getStack('[REDIRECT-TRAP] timer stack');
  if (stackMatched(timerStack)) return true;
  return callbackMatched(callback);
}

含义:只拦截反调试/内存炸弹定时器,不再误伤 Vue/路由/埋点的正常定时器。

3.6 当前状态

  • 打开 DevTools 后不再被强制刷新或重定向。  
  • 页面能正常加载和操作。  
  • 如后续站点升级 hash 或策略,按同样方法重新抓栈和收敛规则即可。

免费评分

参与人数 18威望 +1 吾爱币 +36 热心值 +18 收起 理由
BrutusScipio + 1 + 1 用心讨论,共获提升!
liuxuming3303 + 1 + 1 谢谢@Thanks!
涛之雨 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
aihetianshui + 1 + 1 我很赞同!
Miracle-紫狼 + 1 + 1 谢谢@Thanks!
ytfh1131 + 1 + 1 谢谢@Thanks!
chengdragon + 1 + 1 感谢分享,不明觉厉
weidechan + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
唐小样儿 + 1 + 1 我很赞同!
wahahehe + 1 + 1 我很赞同!
夸克逃逸 + 2 + 1 不明觉厉
cyz98 + 1 谢谢@Thanks!
Bizhi-1024 + 1 谢谢@Thanks!
elan + 1 + 1 用心讨论,共获提升!
gun008 + 1 + 1 用心讨论,共获提升!
buluo533 + 1 + 1 用心讨论,共获提升!
xuhao4577062311 + 1 + 1 我很赞同!
laozhang4201 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
或者8 发表于 2026-3-9 08:22
没看懂!!!!!!!!!!!!
3#
xjcyxyx 发表于 2026-3-9 08:37
4#
vdycmx 发表于 2026-3-9 09:10
5#
loikee 发表于 2026-3-9 15:46
这么厉害
6#
blueluckycard 发表于 2026-3-9 18:29
学习一下。感谢了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-10 04:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表