export class AntiDebug { constructor() { this.checks = []; this.isDestroyed = false; this.originalScripts = new Map(); this.detectionCount = 0; this.monitoringIntervals = []; // 감지 시작(개발자 모드에서는 주석처리) // this.emergencyProtection(); // this.init(); } emergencyProtection() { // 페이지 로드 즉시 실행 const script = document.createElement('script'); script.textContent = ` (function() { const start = Date.now(); debugger; if (Date.now() - start > 100) { document.documentElement.innerHTML = '

Access Denied

'; while(true) { debugger; } } })(); `; if (document.head) { document.head.insertBefore(script, document.head.firstChild); } } async init() { // 모든 스크립트의 실제 내용을 메모리에 저장 await this.captureAllScriptContents(); // 체크 메서드 등록 this.checks.push(this.checkConsole.bind(this)); this.checks.push(this.checkDebugger.bind(this)); this.checks.push(this.checkToString.bind(this)); // this.checks.push(this.checkDevToolsSize.bind(this)); //사이즈는 사이니지때문에 우선 주석 // 다층 보호 시작 this.protectFunctions(); this.blockDevToolsShortcuts(); this.overrideNetworkAPIs(); this.polluteDOMWithFakes(); // 체크 시작 this.startAggressiveMonitoring(); } async captureAllScriptContents() { try { const scripts = document.querySelectorAll('script'); for (const script of scripts) { if (script.src) { try { // 외부 스크립트 fetch const response = await fetch(script.src); const content = await response.text(); this.originalScripts.set(script.src, { type: 'external', content: content, element: script, url: script.src }); } catch(e) { console.error('Failed to capture:', script.src); } } else if (script.textContent) { this.originalScripts.set(`inline_${Date.now()}_${Math.random()}`, { type: 'inline', content: script.textContent, element: script }); } } } catch(e) { console.error('Script capture failed:', e); } } startAggressiveMonitoring() { // 다양한 간격으로 체크 (회피 어렵게) const intervals = [500, 1000, 1500, 2000, 3000]; intervals.forEach(interval => { const id = setInterval(() => this.runChecks(), interval); this.monitoringIntervals.push(id); }); // requestAnimationFrame으로도 체크 const rafCheck = () => { if (!this.isDestroyed) { this.runChecks(); requestAnimationFrame(rafCheck); } }; requestAnimationFrame(rafCheck); } checkConsole() { try { const start = performance.now(); const devtools = {}; Object.defineProperty(devtools, 'toString', { get: function() { return true; } }); console.log('%c', devtools); const end = performance.now(); return (end - start) > 100; } catch(e) { return false; } } checkDebugger() { try { const start = performance.now(); debugger; const end = performance.now(); return (end - start) > 100; } catch(e) { return false; } } checkToString() { try { let detected = false; const detector = /./; detector.toString = function() { detected = true; }; console.log('%c', detector); return detected; } catch(e) { return false; } } checkDevToolsSize() { const widthThreshold = window.outerWidth - window.innerWidth > 160; const heightThreshold = window.outerHeight - window.innerHeight > 160; return widthThreshold || heightThreshold; } protectFunctions() { try { const originalToString = Function.prototype.toString; const self = this; Function.prototype.toString = function() { const fnString = originalToString.call(this); const fnName = this.name || ''; // AntiDebug 관련 함수는 숨김 if (fnName.includes('check') || fnName.includes('Anti') || fnName.includes('destroy') || fnName.includes('protect') || fnString.includes('debugger')) { return `function ${fnName}() { [native code] }`; } return fnString; }; // Object.defineProperty도 보호 const originalDefineProperty = Object.defineProperty; Object.defineProperty = function(obj, prop, descriptor) { // console 조작 방지 if (obj === console || obj === window.console) { return obj; } return originalDefineProperty(obj, prop, descriptor); }; } catch(e) {} } blockDevToolsShortcuts() { // 모든 개발자 도구 단축키 차단 document.addEventListener('keydown', (e) => { const forbidden = [ e.keyCode === 123, // F12 e.ctrlKey && e.shiftKey && e.keyCode === 73, // Ctrl+Shift+I e.ctrlKey && e.shiftKey && e.keyCode === 74, // Ctrl+Shift+J e.ctrlKey && e.shiftKey && e.keyCode === 67, // Ctrl+Shift+C e.ctrlKey && e.keyCode === 85, // Ctrl+U e.metaKey && e.altKey && e.keyCode === 73, // Cmd+Option+I (Mac) e.metaKey && e.altKey && e.keyCode === 74, // Cmd+Option+J (Mac) e.metaKey && e.altKey && e.keyCode === 67, // Cmd+Option+C (Mac) ]; if (forbidden.some(x => x)) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); // this.increaseDetectionCount(); return false; } }, true); // 우클릭 완전 차단 // document.addEventListener('contextmenu', (e) => { // e.preventDefault(); // e.stopPropagation(); // return false; // }, true); // 선택 차단 // document.addEventListener('selectstart', (e) => { // e.preventDefault(); // return false; // }); // 드래그 차단 // document.addEventListener('dragstart', (e) => { // e.preventDefault(); // return false; // }); } overrideNetworkAPIs() { // fetch API 오버라이드 const originalFetch = window.fetch; const self = this; window.fetch = async function(...args) { const url = args[0]; // DevTools가 감지된 경우 if (self.isDestroyed && typeof url === 'string' && url.endsWith('.js')) { console.log('Blocked fetch request:', url); // 가짜 응답 반환 return new Response( self.generateMassiveGarbageCode(), { status: 200, headers: { 'Content-Type': 'application/javascript' } } ); } return originalFetch.apply(this, args); }; // XMLHttpRequest 오버라이드 const originalXHROpen = XMLHttpRequest.prototype.open; const originalXHRSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url, ...rest) { this._url = url; return originalXHROpen.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.send = function(...args) { if (self.isDestroyed && this._url && this._url.endsWith('.js')) { console.log('Blocked XHR request:', this._url); // 가짜 응답 설정 Object.defineProperty(this, 'responseText', { writable: true, value: self.generateMassiveGarbageCode() }); Object.defineProperty(this, 'response', { writable: true, value: self.generateMassiveGarbageCode() }); // 성공 이벤트 발생 setTimeout(() => { this.readyState = 4; this.status = 200; if (this.onload) this.onload(); if (this.onreadystatechange) this.onreadystatechange(); }, 0); return; } return originalXHRSend.apply(this, args); }; } polluteDOMWithFakes() { const fakeCount = 200; // 200개의 가짜 스크립트 const scriptPaths = [ 'main/main.js', 'main/app.js', 'main/utils.js', 'main/api.js', 'main/config.js', 'login/login.js', 'login/auth.js', 'login/validator.js', 'index/index.js', 'index/controller.js', 'index/router.js' ]; // 각 파일명마다 여러 버전의 가짜 생성 scriptPaths.forEach(path => { for (let i = 0; i < 20; i++) { const fakeScript = document.createElement('script'); fakeScript.setAttribute('data-fake-source', path); fakeScript.setAttribute('data-version', `v${i}`); fakeScript.type = 'text/javascript'; fakeScript.textContent = this.generateRealisticFakeCode(path, i); if (document.head) { document.head.appendChild(fakeScript); } } }); } generateRealisticFakeCode(filename, version) { const modules = ['auth', 'api', 'utils', 'config', 'router', 'store']; const functions = ['init', 'setup', 'validate', 'process', 'handle', 'execute']; return ` // =================================================================== // Module: ${filename} (Protected Version ${version}) // Build: ${Math.random().toString(36).substr(2, 10).toUpperCase()} // Status: ENCRYPTED - Original source removed // =================================================================== (function(global, factory) { 'use strict'; if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory(); } else { global._Module_${Math.random().toString(36).substr(2, 8)} = factory(); } }(typeof window !== 'undefined' ? window : this, function() { 'use strict'; // ============ Obfuscated Configuration ============ const _config_${Math.random().toString(36).substr(2, 6)} = { ${this.generateFakeConfig()} }; // ============ Protected Functions ============ ${functions.map(fn => this.generateFakeFunction(fn)).join('\n\n')} // ============ Anti-Tampering ============ const _protect = function() { try { debugger; const _check = function() { if (window.console || window.devtools) { debugger; } }; setInterval(_check, ${Math.floor(Math.random() * 50) + 30}); } catch(e) {} }; _protect(); // ============ Encrypted Variables ============ ${this.generateEncryptedVars(50)} // ============ Return Protected Module ============ return Object.freeze({ version: '${version}.0.0', protected: true, ${functions.map(fn => `${fn}: _fn_${fn}`).join(',\n ')} }); })); // Additional protection layer ${this.generateComplexGarbageCode()} `; } generateFakeConfig() { const configs = []; for (let i = 0; i < 15; i++) { configs.push(` key_${i}: "${Math.random().toString(36).substr(2, 12)}"`); } return configs.join(',\n'); } generateFakeFunction(name) { return ` const _fn_${name} = function(params) { const _internal_${Math.random().toString(36).substr(2, 6)} = params || {}; // Protected logic ${Array(5).fill(0).map((_, i) => `const _var${i} = "${Math.random().toString(36).substr(2, 8)}";` ).join('\n ')} try { debugger; } catch(e) {} return _internal_${Math.random().toString(36).substr(2, 6)}; };`; } generateEncryptedVars(count) { let vars = []; for (let i = 0; i < count; i++) { vars.push(` const _0x${Math.random().toString(36).substr(2, 8)} = ${Math.random()};`); } return vars.join('\n'); } generateComplexGarbageCode() { return ` (function() { const _obf = { ${Array(30).fill(0).map(() => `_${Math.random().toString(36).substr(2, 8)}: "${Math.random().toString(36).substr(2)}"` ).join(',\n ')} }; setInterval(() => { try { debugger; } catch(e) {} }, ${Math.floor(Math.random() * 100) + 50}); })(); `; } generateMassiveGarbageCode() { let code = ` // ============================================ // PROTECTED SOURCE - ACCESS DENIED // ============================================ // This is not the original source code // All sources have been encrypted // Timestamp: ${Date.now()} // ============================================ `; // 대량의 가짜 코드 생성 for (let i = 0; i < 100; i++) { code += `const _obf_${i}_${Math.random().toString(36).substr(2, 8)} = ${Math.random()};\n`; } code += `\n(function() {\n`; code += ` 'use strict';\n\n`; for (let i = 0; i < 50; i++) { code += ` var _${Math.random().toString(36).substr(2, 8)} = "${Math.random().toString(36).substr(2)}";\n`; } code += `\n setInterval(function() {\n`; code += ` debugger;\n`; code += ` }, 50);\n`; code += `\n (function infiniteDebugger() {\n`; code += ` try {\n`; code += ` debugger;\n`; code += ` setTimeout(infiniteDebugger, 10);\n`; code += ` } catch(e) {}\n`; code += ` })();\n`; code += `})();\n`; return code; } increaseDetectionCount() { this.detectionCount++; if (this.detectionCount >= 2) { this.onDetected(); } } runChecks() { if (this.isDestroyed) return; let detectedCount = 0; for (let check of this.checks) { try { if (check()) { detectedCount++; } } catch(e) {} } if (detectedCount >= 2) { this.onDetected(); } } nukeAllScripts() { try { // 1. 모든 기존 script 태그의 내용을 가짜로 교체 const allScripts = document.querySelectorAll('script'); allScripts.forEach(script => { if (script.src) { // src가 있는 스크립트는 제거하고 inline으로 가짜 삽입 const fakeInline = document.createElement('script'); fakeInline.textContent = this.generateMassiveGarbageCode(); fakeInline.setAttribute('data-original-src', script.src); if (script.parentNode) { script.parentNode.replaceChild(fakeInline, script); } } else { // inline 스크립트는 내용만 교체 script.textContent = this.generateMassiveGarbageCode(); } }); // 2. 추가로 대량의 가짜 스크립트 주입 (이미 polluteDOMWithFakes에서 했지만 더 추가) for (let i = 0; i < 100; i++) { const fakeScript = document.createElement('script'); fakeScript.textContent = this.generateMassiveGarbageCode(); fakeScript.setAttribute('data-protection-layer', i); if (document.body) { document.body.appendChild(fakeScript); } } } catch(e) { console.error('Script nuking failed:', e); } } destroyDOMCompletely() { try { // DOM 전체를 새로운 내용으로 교체 const newHTML = ` Access Denied

⚠ ACCESS DENIED ⚠

DEVELOPER TOOLS DETECTED

All source code has been encrypted

Unauthorized access logged

${this.generateInlineProtectionScripts()} `; // 문서 전체 교체 document.open(); document.write(newHTML); document.close(); } catch(e) { console.error('DOM destruction failed:', e); } } generateInlineProtectionScripts() { let scripts = ''; // 10개의 보호 스크립트 레이어 for (let i = 0; i < 10; i++) { scripts += ` `; } return scripts; } createMultiLayerDebuggerTrap() { // 레이어 1: 초고속 interval setInterval(() => { if (this.isDestroyed) debugger; }, 10); // 레이어 2: 중속 interval setInterval(() => { if (this.isDestroyed) debugger; }, 50); // 레이어 3: 저속 interval setInterval(() => { if (this.isDestroyed) debugger; }, 200); // 레이어 4: setTimeout 재귀 const timeoutTrap = () => { if (this.isDestroyed) { debugger; setTimeout(timeoutTrap, 30); } }; timeoutTrap(); // 레이어 5: requestAnimationFrame const rafTrap = () => { if (this.isDestroyed) { debugger; requestAnimationFrame(rafTrap); } }; requestAnimationFrame(rafTrap); // 레이어 6: Promise 체인 const promiseTrap = () => { if (this.isDestroyed) { debugger; Promise.resolve().then(promiseTrap); } }; promiseTrap(); // 레이어 7: setImmediate (Node.js style, 있으면) if (typeof setImmediate !== 'undefined') { const immediateTrap = () => { if (this.isDestroyed) { debugger; setImmediate(immediateTrap); } }; immediateTrap(); } // 레이어 8: 최후의 무한 루프 (3초 후 실행) setTimeout(() => { if (this.isDestroyed) { while(true) { debugger; } } }, 3000); } lockPageCompletely() { try { // 뒤로가기 완전 차단 history.pushState(null, null, location.href); window.onpopstate = function() { history.go(1); }; // 새로고침 차단 window.onbeforeunload = function(e) { e.preventDefault(); return "Developer tools detected"; }; // 모든 키보드 입력 차단 document.addEventListener('keydown', (e) => { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); return false; }, true); // 모든 마우스 이벤트 차단 ['click', 'mousedown', 'mouseup', 'dblclick', 'contextmenu'].forEach(eventType => { document.addEventListener(eventType, (e) => { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); return false; }, true); }); // 모든 터치 이벤트 차단 ['touchstart', 'touchend', 'touchmove'].forEach(eventType => { document.addEventListener(eventType, (e) => { e.preventDefault(); e.stopPropagation(); return false; }, true); }); } catch(e) {} } onDetected() { if (this.isDestroyed) return; console.clear(); // 상태 플래그 설정 this.isDestroyed = true; // 모든 모니터링 interval 중지 this.monitoringIntervals.forEach(id => clearInterval(id)); this.monitoringIntervals = []; // 경고 // alert('⚠️ Developer Tools Detected!\n\nPM에서는 개발자툴 사용이 불가능합니다.'); // 실행 순서 (매우 중요!) // 1단계: 모든 스크립트 즉시 파괴 this.nukeAllScripts(); // 2단계: 네트워크 요청 오버라이드 (이미 init에서 했지만 재확인) this.overrideNetworkAPIs(); // 3단계: 페이지 완전 잠금 this.lockPageCompletely(); // 4단계: 다층 디버거 트랩 시작 this.createMultiLayerDebuggerTrap(); // 5단계: DOM 완전 파괴 (선택적 - 매우 공격적) setTimeout(() => { this.destroyDOMCompletely(); }, 1000); // 6단계: localStorage에 감지 기록 try { localStorage.setItem('devtools_detected', Date.now().toString()); localStorage.setItem('access_blocked', 'true'); } catch(e) {} } }