Skip to content

js逆向-控制台呼出检测

前言

控制台呼出检测是我们JS逆向工作中经常遇到的防护手段

控制台呼出检测全面解析

一、检测原理与基础机制

控制台呼出检测的核心原理是基于浏览器行为差异和环境特征变化。当开发者工具打开时,浏览器会表现出多种可检测的特征变化。

1.1 视口尺寸检测原理

当控制台打开时,浏览器可视区域(viewport)的尺寸会发生变化,这是最直接的检测方式:

js
class ViewportDetector {
    constructor() {
        this.originalWidth = window.visualViewport.width;
        this.originalHeight = window.visualViewport.height;
        this.detectionEnabled = false;
    }

    startDetection() {
        this.detectionEnabled = true;
        setInterval(() => {
            const currentWidth = window.visualViewport.width;
            const currentHeight = window.visualViewport.height;

            if (currentWidth < this.originalWidth ||
                currentHeight < this.originalHeight) {
                this.onConsoleDetected('viewport_change');
            }
        }, 500);
    }

    onConsoleDetected(reason) {
        console.log(`控制台检测通过: ${reason}`);
        // 执行防护动作,如刷新页面、跳转或警告
        window.location.reload();
    }
}

1.2 控制台API监控

js
网站可以重写console对象的方法来检测控制台调用:
const consoleMonitor = (function() {
const originalConsole = {...console};
const callStack = [];

    // 重写所有console方法
    ['log', 'error', 'warn', 'info', 'debug'].forEach(method => {
        console[method] = function(...args) {
            // 记录调用信息
            callStack.push({
                method,
                arguments: args,
                timestamp: Date.now(),
                stack: new Error().stack
            });
            
            // 执行原始操作
            originalConsole[method].apply(console, args);
            
            // 检测到控制台活动
            if (callStack.length > 10) { // 阈值检测
                triggerProtection('console_flooding');
            }
        };
    });
    
    return {callStack, originalConsole};
})();

二、高级检测技术

2.1 性能时序检测

利用debugger语句执行时的时间差异进行检测:

js
class PerformanceDetector {
constructor() {
this.threshold = 100; // 毫秒阈值
}

    async checkDebuggerPerformance() {
        const startTime = performance.now();
        
        // 创建debugger检测点
        try {
            // 方法1: 直接debugger
            debugger;
        } catch (e) {}
        
        const endTime = performance.now();
        const executionTime = endTime - startTime;
        
        if (executionTime > this.threshold) {
            this.onDebuggerDetected(executionTime);
            return true;
        }
        return false;
    }
    
    async advancedCheck() {
        // 多次检测提高准确性
        const results = [];
        for (let i = 0; i < 5; i++) {
            results.push(await this.checkDebuggerPerformance());
        }
        
        const detectedCount = results.filter(Boolean).length;
        return detectedCount >= 3; // 5次中3次检测到即为真
    }
    
    onDebuggerDetected(executionTime) {
        console.warn(`检测到调试器,执行时间: ${executionTime}ms`);
        // 防护逻辑
    }
}

2.2 异步调试器检测 更高级的检测使用异步和动态方法:

js
class AdvancedDebuggerDetector {
constructor() {
this.uniqueIdentifier = 0;
this.detectionMethods = [];
}

    // 动态debugger生成
    createDynamicDebugger() {
        const uniqueId = this.uniqueIdentifier++;
        const debuggerCode = `debugger;/*# sourceURL=dynamic-debugger-${uniqueId}.js*/`;
        
        // 多种执行方式
        const executers = [
            () => Function(debuggerCode)(),
            () => eval(debuggerCode),
            () => (0, eval)(debuggerCode), // 间接eval
            () => {
                const script = document.createElement('script');
                script.textContent = debuggerCode;
                document.head.appendChild(script);
                document.head.removeChild(script);
            }
        ];
        
        return executers[Math.floor(Math.random() * executers.length)];
    }
    
    // 启动综合检测
    startComprehensiveDetection() {
        // 定时检测
        setInterval(() => {
            const dynamicDebugger = this.createDynamicDebugger();
            try {
                dynamicDebugger();
            } catch (e) {
                // 异常处理
            }
        }, 1000);
        
        // 性能监控
        this.monitorPerformance();
    }
    
    monitorPerformance() {
        let lastCheck = performance.now();
        
        setInterval(() => {
            const currentTime = performance.now();
            const timeDiff = currentTime - lastCheck;
            
            // 检测时间异常
            if (timeDiff > 2000) { // 2秒间隔异常
                this.onSuspiciousActivity('time_anomaly');
            }
            
            lastCheck = currentTime;
        }, 100);
    }
}

三、DOM与事件监测

3.1 键盘事件监听

检测开发者工具快捷键:

js
class KeyboardEventMonitor {
constructor() {
this.keyHistory = [];
this.ctrlKeyPressed = false;
this.shiftKeyPressed = false;

        this.initializeEventListeners();
    }
    
    initializeEventListeners() {
        document.addEventListener('keydown', (event) => {
            this.handleKeyDown(event);
        });
        
        document.addEventListener('keyup', (event) => {
            this.handleKeyUp(event);
        });
        
        // 防止右键菜单检查
        document.addEventListener('contextmenu', (event) => {
            this.handleContextMenu(event);
        });
    }
    
    handleKeyDown(event) {
        const key = event.key.toLowerCase();
        
        // 检测F12
        if (key === 'f12' || event.keyCode === 123) {
            event.preventDefault();
            this.onDeveloperShortcut('f12');
            return;
        }
        
        // 检测Ctrl+Shift+I
        if (event.ctrlKey && event.shiftKey && key === 'i') {
            event.preventDefault();
            this.onDeveloperShortcut('ctrl_shift_i');
            return;
        }
        
        // 检测Ctrl+Shift+J
        if (event.ctrlKey && event.shiftKey && key === 'j') {
            event.preventDefault();
            this.onDeveloperShortcut('ctrl_shift_j');
            return;
        }
        
        // 检测Ctrl+Shift+C
        if (event.ctrlKey && event.shiftKey && key === 'c') {
            event.preventDefault();
            this.onDeveloperShortcut('ctrl_shift_c');
            return;
        }
    }
    
    onDeveloperShortcut(shortcut) {
        console.warn(`检测到开发者工具快捷键: ${shortcut}`);
        this.executeCounterMeasures();
    }
    
    executeCounterMeasures() {
        // 多种应对策略
        const strategies = [
            () => window.location.reload(),
            () => { throw new Error('Debugging not allowed'); },
            () => this.startMemoryBomb(),
            () => this.redirectToConfusion()
        ];
        
        const randomStrategy = strategies[Math.floor(Math.random() * strategies.length)];
        randomStrategy();
    }
    
    startMemoryBomb() {
        // 内存压力策略
        const largeArray = [];
        for (let i = 0; i < 100000; i++) {
            largeArray.push(new Array(1000).fill('*'));
        }
    }
}

四、反检测与绕过技术

4.1 基础绕过方法

针对常见检测手段的应对策略:

js
class ConsoleProtectionBypass {
constructor() {
this.originalFunctions = new Map();
this.initialized = false;
}

    initialize() {
        if (this.initialized) return;
        
        this.restoreConsoleMethods();
        this.bypassDebuggerTraps();
        this.disablePerformanceMonitoring();
        this.restoreEventListeners();
        
        this.initialized = true;
    }
    
    // 恢复console方法
    restoreConsoleMethods() {
        const consoleMethods = ['log', 'error', 'warn', 'info', 'debug', 'table'];
        
        consoleMethods.forEach(method => {
            if (console[method] && console[method].restore) {
                console[method].restore();
            }
        });
    }
    
    // 绕过debugger陷阱
    bypassDebuggerTraps() {
        // 方法1: 重写Function构造函数
        const originalFunction = Function.prototype.constructor;
        Function.prototype.constructor = function(...args) {
            if (args.length > 0 && typeof args[0] === 'string' && 
                args[0].includes('debugger')) {
                console.log('拦截debugger语句:', args[0]);
                return function() {};
            }
            return originalFunction.apply(this, args);
        };
        
        // 方法2: 禁用特定断点
        this.disableSpecificBreakpoints();
    }
    
    disableSpecificBreakpoints() {
        // 使用断点管理API
        if (typeof debugger !== 'undefined') {
            // 动态脚本注入绕过
            const script = document.createElement('script');
            script.textContent = `
                window.__originalDebugger = debugger;
                debugger = function() { /* 空函数 */ };
            `;
            document.head.appendChild(script);
        }
    }
    
    // 禁用性能监控
    disablePerformanceMonitoring() {
        // 重写performance方法
        const originalNow = performance.now;
        let timeOffset = 0;
        
        performance.now = function() {
            return originalNow.call(performance) - timeOffset;
        };
        
        // 随机时间偏移
        setInterval(() => {
            timeOffset += Math.random() * 10;
        }, 1000);
    }
}

4.2 高级绕过技术

针对复杂检测系统的应对方案:

js
class AdvancedBypass {
constructor() {
this.hooks = new Map();
}

    // 函数重写hook
    hookFunction(target, methodName, replacement) {
        const original = target[methodName];
        const self = this;
        
        target[methodName] = function(...args) {
            // 调用前置处理
            const preResult = self.beforeCall(methodName, args);
            if (preResult.handled) return preResult.returnValue;
            
            // 调用原始函数或替换函数
            const result = replacement.call(this, original, ...args);
            
            // 调用后置处理
            const postResult = self.afterCall(methodName, result, args);
            return postResult.returnValue;
        };
        
        this.hooks.set(methodName, {original, replacement});
    }
    
    // 代理整个对象
    createProxy(target) {
        const handler = {
            get: (obj, prop) => {
                if (prop === 'debugger') {
                    return function() {}; // 空函数
                }
                
                const value = obj[prop];
                return typeof value === 'function' ? value.bind(obj) : value;
            },
            
            set: (obj, prop, value) => {
                if (typeof value === 'function' && 
                    value.toString().includes('debugger')) {
                    console.log('拦截debugger函数设置:', prop);
                    return true; // 阻止设置
                }
                obj[prop] = value;
                return true;
            }
        };
        
        return new Proxy(target, handler);
    }
    
    // 反检测代码注入
    injectAntiDetection() {
        const styles = `
            <style id="anti-detection-styles">
                /* 隐藏检测元素 */
                .detection-element { display: none !important; }
                /* 防止尺寸检测 */
                body { min-width: 100vw !important; min-height: 100vh !important; }
            </style>
        `;
        
        const script = `
            <script>
                // 覆盖检测变量
                window.__isDeveloperToolsOpen = false;
                window.visualViewport = new Proxy(window.visualViewport, {
                    get: (target, prop) => {
                        if (prop === 'width') return window.innerWidth;
                        if (prop === 'height') return window.innerHeight;
                        return target[prop];
                    }
                });
            </script>
        `;
        
        document.head.insertAdjacentHTML('beforeend', styles + script);
    }
}